├── .gitattributes
├── .gitignore
├── README.md
├── bin
├── Simplifynet.dll
└── Simplifynet.pdb
└── src
├── Simplifynet.CSharpPortable
├── ISimplifyUtility.cs
├── Point.cs
├── Properties
│ └── AssemblyInfo.cs
├── SimplifyUtility.cs
├── SimplifyUtility3D.cs
└── Simplifynet.CSharpPortable.csproj
├── Simplifynet.Tests
├── Properties
│ └── AssemblyInfo.cs
├── SimplifyUtilityTests.cs
├── Simplifynet.Tests.csproj
├── data
│ └── LongLine.cs
└── extentions
│ └── nunit.framework.dll
└── Simplifynet.sln
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.sln.docstates
8 |
9 | # Build results
10 |
11 | [Dd]ebug/
12 | [Rr]elease/
13 | x64/
14 | build/
15 | [Oo]bj/
16 |
17 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets
18 | !packages/*/build/
19 |
20 | # MSTest test Results
21 | [Tt]est[Rr]esult*/
22 | [Bb]uild[Ll]og.*
23 |
24 | *_i.c
25 | *_p.c
26 | *.ilk
27 | *.meta
28 | *.obj
29 | *.pch
30 | *.pgc
31 | *.pgd
32 | *.rsp
33 | *.sbr
34 | *.tlb
35 | *.tli
36 | *.tlh
37 | *.tmp
38 | *.tmp_proj
39 | *.log
40 | *.vspscc
41 | *.vssscc
42 | .builds
43 | *.pidb
44 | *.log
45 | *.scc
46 |
47 | # Visual C++ cache files
48 | ipch/
49 | *.aps
50 | *.ncb
51 | *.opensdf
52 | *.sdf
53 | *.cachefile
54 |
55 | # Visual Studio profiler
56 | *.psess
57 | *.vsp
58 | *.vspx
59 |
60 | # Guidance Automation Toolkit
61 | *.gpState
62 |
63 | # ReSharper is a .NET coding add-in
64 | _ReSharper*/
65 | *.[Rr]e[Ss]harper
66 |
67 | # TeamCity is a build add-in
68 | _TeamCity*
69 |
70 | # DotCover is a Code Coverage Tool
71 | *.dotCover
72 |
73 | # NCrunch
74 | *.ncrunch*
75 | .*crunch*.local.xml
76 |
77 | # Installshield output folder
78 | [Ee]xpress/
79 |
80 | # DocProject is a documentation generator add-in
81 | DocProject/buildhelp/
82 | DocProject/Help/*.HxT
83 | DocProject/Help/*.HxC
84 | DocProject/Help/*.hhc
85 | DocProject/Help/*.hhk
86 | DocProject/Help/*.hhp
87 | DocProject/Help/Html2
88 | DocProject/Help/html
89 |
90 | # Click-Once directory
91 | publish/
92 |
93 | # Publish Web Output
94 | *.Publish.xml
95 |
96 | # NuGet Packages Directory
97 | ## TODO: If you have NuGet Package Restore enabled, uncomment the next line
98 | #packages/
99 |
100 | # Windows Azure Build Output
101 | csx
102 | *.build.csdef
103 |
104 | # Windows Store app package directory
105 | AppPackages/
106 |
107 | # Others
108 | sql/
109 | *.Cache
110 | ClientBin/
111 | [Ss]tyle[Cc]op.*
112 | ~$*
113 | *~
114 | *.dbmdl
115 | *.[Pp]ublish.xml
116 | *.pfx
117 | *.publishsettings
118 |
119 | # RIA/Silverlight projects
120 | Generated_Code/
121 |
122 | # Backup & report files from converting an old project file to a newer
123 | # Visual Studio version. Backup files are not needed, because we have git ;-)
124 | _UpgradeReport_Files/
125 | Backup*/
126 | UpgradeLog*.XML
127 | UpgradeLog*.htm
128 |
129 | # SQL Server files
130 | App_Data/*.mdf
131 | App_Data/*.ldf
132 |
133 |
134 | #LightSwitch generated files
135 | GeneratedArtifacts/
136 | _Pvt_Extensions/
137 | ModelManifest.xml
138 |
139 | # =========================
140 | # Windows detritus
141 | # =========================
142 |
143 | # Windows image file caches
144 | Thumbs.db
145 | ehthumbs.db
146 |
147 | # Folder config file
148 | Desktop.ini
149 |
150 | # Recycle Bin used on file shares
151 | $RECYCLE.BIN/
152 |
153 | # Mac desktop service store files
154 | .DS_Store
155 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | simplify-net
2 | ===========
3 |
4 | A .NET port of simplify-js (https://github.com/mourner/simplify-js)
5 | Polyline simplification library by Vladimir Agafonkin, extracted from Leaflet.
6 | For license see the original project or https://github.com/mourner/simplify-js/blob/master/LICENSE
7 |
8 | For more info on the C# .NET ported version visit my blog @ http://shz.no
9 |
10 | Targets
11 | -------
12 | This port is written in C# as a portable library.
13 | It targets: .NET Framework 4.5, Windows 8, Windows Phone 8.1 and Windows Phone Silverlight 8
14 |
15 | Install
16 | -------
17 | Download the precomplied library from the bin folder. See the example for more help!
18 |
19 | Performance
20 | -----------
21 | Performance for this project has absolute been in focus. All of the operations has been tuned down to maximum performance C# can provide.
22 |
23 | Running test
24 | ------------
25 | The tests are also ported from the original project, see own solution with the tests.
26 | I am using NUnit to setup and run the tests, nunit dll is also included.
27 | More about NUnit, go to http://nunit.org
28 |
29 | # Example #
30 |
31 | ```C#
32 | // Some points to test with
33 | var points = new[] {
34 | new Point(224.55,250.15),new Point(226.91,244.19),new Point(233.31,241.45),new Point(234.98,236.06),
35 | new Point(244.21,232.76),new Point(262.59,215.31),new Point(267.76,213.81),new Point(273.57,201.84),
36 | new Point(273.12,192.16),new Point(277.62,189.03),new Point(280.36,181.41),new Point(286.51,177.74),
37 | new Point(292.41,159.37),new Point(296.91,155.64),new Point(314.95,151.37),new Point(319.75,145.16),
38 | new Point(330.33,137.57),new Point(341.48,139.96),new Point(369.98,137.89),new Point(387.39,142.51),
39 | new Point(391.28,139.39),new Point(409.52,141.14),new Point(414.82,139.75),new Point(427.72,127.30),
40 | new Point(439.60,119.74),new Point(474.93,107.87),new Point(486.51,106.75),new Point(489.20,109.45),
41 | new Point(493.79,108.63),new Point(504.74,119.66),new Point(512.96,122.35),new Point(518.63,120.89),
42 | new Point(524.09,126.88),new Point(529.57,127.86),new Point(534.21,140.93),new Point(539.27,147.24),
43 | new Point(567.69,148.91),new Point(575.25,157.26),new Point(580.62,158.15),new Point(601.53,156.85),
44 | new Point(617.74,159.86),new Point(622.00,167.04),new Point(629.55,194.60),new Point(638.90,195.61),
45 | new Point(641.26,200.81),new Point(651.77,204.56),new Point(671.55,222.55),new Point(683.68,217.45),
46 | new Point(695.25,219.15),new Point(700.64,217.98),new Point(703.12,214.36),new Point(712.26,215.87),
47 | new Point(721.49,212.81),new Point(727.81,213.36),new Point(729.98,208.73),new Point(735.32,208.20),
48 | new Point(739.94,204.77),new Point(769.98,208.42),new Point(779.60,216.87),new Point(784.20,218.16),
49 | new Point(800.24,214.62),new Point(810.53,219.73),new Point(817.19,226.82),new Point(820.77,236.17),
50 | new Point(827.23,236.16),new Point(829.89,239.89),new Point(851.00,248.94),new Point(859.88,255.49),
51 | new Point(865.21,268.53),new Point(857.95,280.30),new Point(865.48,291.45),new Point(866.81,298.66),
52 | new Point(864.68,302.71),new Point(867.79,306.17),new Point(859.87,311.37),new Point(860.08,314.35),
53 | new Point(858.29,314.94),new Point(858.10,327.60),new Point(854.54,335.40),new Point(860.92,343.00),
54 | new Point(856.43,350.15),new Point(851.42,352.96),new Point(849.84,359.59),new Point(854.56,365.53),
55 | new Point(849.74,370.38),new Point(844.09,371.89),new Point(844.75,380.44),new Point(841.52,383.67),
56 | new Point(839.57,390.40),new Point(845.59,399.05),new Point(848.40,407.55),new Point(843.71,411.30),
57 | new Point(844.09,419.88),new Point(839.51,432.76),new Point(841.33,441.04),new Point(847.62,449.22),
58 | new Point(847.16,458.44),new Point(851.38,462.79),new Point(853.97,471.15),new Point(866.36,480.77)
59 | };
60 |
61 | // After simplification these points should be returned
62 | var simplified = new[] {
63 | new Point(224.55,250.15),new Point(267.76,213.81),new Point(296.91,155.64),new Point(330.33,137.57),
64 | new Point(409.52,141.14),new Point(439.60,119.74),new Point(486.51,106.75),new Point(529.57,127.86),
65 | new Point(539.27,147.24),new Point(617.74,159.86),new Point(629.55,194.60),new Point(671.55,222.55),
66 | new Point(727.81,213.36),new Point(739.94,204.77),new Point(769.98,208.42),new Point(779.60,216.87),
67 | new Point(800.24,214.62),new Point(820.77,236.17),new Point(859.88,255.49),new Point(865.21,268.53),
68 | new Point(857.95,280.30),new Point(867.79,306.17),new Point(859.87,311.37),new Point(854.54,335.40),
69 | new Point(860.92,343.00),new Point(849.84,359.59),new Point(854.56,365.53),new Point(844.09,371.89),
70 | new Point(839.57,390.40),new Point(848.40,407.55),new Point(839.51,432.76),new Point(853.97,471.15),
71 | new Point(866.36,480.77)};
72 |
73 | var utility = new SimplifyUtility(); // Or use the 3D utility: new SimplifyUtility3D();
74 |
75 | var result = utility.Simplify(points, 5, false);
76 |
77 | Assert.AreEqual(simplified.Length, result.Count);
78 | Assert.That(simplified, Is.EquivalentTo(result));
79 | ```
80 |
--------------------------------------------------------------------------------
/bin/Simplifynet.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imshz/simplify-net/82d0c7289a17c29698562fe0687a9a4949ae2294/bin/Simplifynet.dll
--------------------------------------------------------------------------------
/bin/Simplifynet.pdb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imshz/simplify-net/82d0c7289a17c29698562fe0687a9a4949ae2294/bin/Simplifynet.pdb
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/ISimplifyUtility.cs:
--------------------------------------------------------------------------------
1 | // High-performance polyline simplification library
2 | //
3 | // This is a port of simplify-js by Vladimir Agafonkin, Copyright (c) 2012
4 | // https://github.com/mourner/simplify-js
5 | //
6 | // The code is ported from JavaScript to C#.
7 | // The library is created as portable and
8 | // is targeting multiple Microsoft plattforms.
9 | //
10 | // This library was ported by imshz @ http://www.shz.no
11 | // https://github.com/imshz/simplify-net
12 | //
13 | // This code is provided as is by the author. For complete license please
14 | // read the original license at https://github.com/mourner/simplify-js
15 |
16 | using System.Collections.Generic;
17 |
18 | namespace Simplifynet
19 | {
20 | public interface ISimplifyUtility
21 | {
22 | ///
23 | /// Simplifies a list of points to a shorter list of points.
24 | ///
25 | /// Points original list of points
26 | /// Tolerance tolerance in the same measurement as the point coordinates
27 | /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm
28 | /// Simplified list of points
29 | List Simplify(Point[] points, double tolerance = 0.3, bool highestQuality = false);
30 | }
31 | }
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/Point.cs:
--------------------------------------------------------------------------------
1 | // High-performance polyline simplification library
2 | //
3 | // This is a port of simplify-js by Vladimir Agafonkin, Copyright (c) 2012
4 | // https://github.com/mourner/simplify-js
5 | //
6 | // The code is ported from JavaScript to C#.
7 | // The library is created as portable and
8 | // is targeting multiple Microsoft plattforms.
9 | //
10 | // This library was ported by imshz @ http://www.shz.no
11 | // https://github.com/imshz/simplify-net
12 | //
13 | // This code is provided as is by the author. For complete license please
14 | // read the original license at https://github.com/mourner/simplify-js
15 |
16 | using System;
17 |
18 | namespace Simplifynet
19 | {
20 | public class Point : IEquatable
21 | {
22 | public double X;
23 | public double Y;
24 | public double Z;
25 |
26 | public Point(double x, double y, double z = 0)
27 | {
28 | X = x;
29 | Y = y;
30 | Z = z;
31 | }
32 |
33 | public bool IsValid
34 | {
35 | get
36 | {
37 | return ((((X <= 90.0) && (Y >= -90.0)) && (Y <= 180.0)) && (X >= -180.0));
38 | }
39 | }
40 |
41 | public override bool Equals(object obj)
42 | {
43 | if (ReferenceEquals(null, obj)) return false;
44 | if (ReferenceEquals(this, obj)) return true;
45 | if (obj.GetType() != typeof(Point) && obj.GetType() != typeof(Point))
46 | return false;
47 | return Equals(obj as Point);
48 | }
49 |
50 | public bool Equals(Point other)
51 | {
52 | if (ReferenceEquals(null, other)) return false;
53 | if (ReferenceEquals(this, other)) return true;
54 | return other.X.Equals(X) && other.Y.Equals(Y) && other.Z.Equals(Z);
55 | }
56 |
57 | public override int GetHashCode()
58 | {
59 | unchecked
60 | {
61 | return (X.GetHashCode() * 397) ^ Y.GetHashCode() ^ Z.GetHashCode();
62 | }
63 | }
64 |
65 | public override string ToString()
66 | {
67 | return string.Format("{0} {1} {2}", X, Y, Z);
68 | }
69 | }
70 | }
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Resources;
2 | using System.Reflection;
3 | using System.Runtime.CompilerServices;
4 | using System.Runtime.InteropServices;
5 |
6 | // General Information about an assembly is controlled through the following
7 | // set of attributes. Change these attribute values to modify the information
8 | // associated with an assembly.
9 | [assembly: AssemblyTitle("Simplifynet.CSharpPortable")]
10 | [assembly: AssemblyDescription("")]
11 | [assembly: AssemblyConfiguration("")]
12 | [assembly: AssemblyCompany("")]
13 | [assembly: AssemblyProduct("Simplifynet.CSharpPortable")]
14 | [assembly: AssemblyCopyright("Copyright © 2014")]
15 | [assembly: AssemblyTrademark("")]
16 | [assembly: AssemblyCulture("")]
17 | [assembly: NeutralResourcesLanguage("en")]
18 |
19 | // Version information for an assembly consists of the following four values:
20 | //
21 | // Major Version
22 | // Minor Version
23 | // Build Number
24 | // Revision
25 | //
26 | // You can specify all the values or you can default the Build and Revision Numbers
27 | // by using the '*' as shown below:
28 | // [assembly: AssemblyVersion("1.0.*")]
29 | [assembly: AssemblyVersion("1.0.0.0")]
30 | [assembly: AssemblyFileVersion("1.0.0.0")]
31 |
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/SimplifyUtility.cs:
--------------------------------------------------------------------------------
1 | // High-performance polyline simplification library
2 | //
3 | // This is a port of simplify-js by Vladimir Agafonkin, Copyright (c) 2012
4 | // https://github.com/mourner/simplify-js
5 | //
6 | // The code is ported from JavaScript to C#.
7 | // The library is created as portable and
8 | // is targeting multiple Microsoft plattforms.
9 | //
10 | // This library was ported by imshz @ http://www.shz.no
11 | // https://github.com/imshz/simplify-net
12 | //
13 | // This code is provided as is by the author. For complete license please
14 | // read the original license at https://github.com/mourner/simplify-js
15 |
16 | using System.Collections.Generic;
17 |
18 | namespace Simplifynet
19 | {
20 | ///
21 | /// Simplification of a 2D-polyline.
22 | ///
23 | public class SimplifyUtility : ISimplifyUtility
24 | {
25 | // square distance between 2 points
26 | private double GetSquareDistance(Point p1, Point p2)
27 | {
28 | double dx = p1.X - p2.X,
29 | dy = p1.Y - p2.Y;
30 |
31 | return (dx*dx) + (dy*dy);
32 | }
33 |
34 | // square distance from a point to a segment
35 | private double GetSquareSegmentDistance(Point p, Point p1, Point p2)
36 | {
37 | var x = p1.X;
38 | var y = p1.Y;
39 | var dx = p2.X - x;
40 | var dy = p2.Y - y;
41 |
42 | if (!dx.Equals(0.0) || !dy.Equals(0.0))
43 | {
44 | var t = ((p.X - x) * dx + (p.Y - y) * dy) / (dx * dx + dy * dy);
45 |
46 | if (t > 1)
47 | {
48 | x = p2.X;
49 | y = p2.Y;
50 | }
51 | else if (t > 0)
52 | {
53 | x += dx*t;
54 | y += dy*t;
55 | }
56 | }
57 |
58 | dx = p.X - x;
59 | dy = p.Y - y;
60 |
61 | return (dx*dx) + (dy*dy);
62 | }
63 |
64 | // rest of the code doesn't care about point format
65 |
66 | // basic distance-based simplification
67 | private List SimplifyRadialDistance(Point[] points, double sqTolerance)
68 | {
69 | var prevPoint = points[0];
70 | var newPoints = new List {prevPoint};
71 | Point point = null;
72 |
73 | for (var i = 1; i < points.Length; i++)
74 | {
75 | point = points[i];
76 |
77 | if (GetSquareDistance(point, prevPoint) > sqTolerance)
78 | {
79 | newPoints.Add(point);
80 | prevPoint = point;
81 | }
82 | }
83 |
84 | if (point != null && !prevPoint.Equals(point))
85 | newPoints.Add(point);
86 |
87 | return newPoints;
88 | }
89 |
90 | // simplification using optimized Douglas-Peucker algorithm with recursion elimination
91 | private List SimplifyDouglasPeucker(Point[] points, double sqTolerance)
92 | {
93 | var len = points.Length;
94 | var markers = new int?[len];
95 | int? first = 0;
96 | int? last = len - 1;
97 | int? index = 0;
98 | var stack = new List();
99 | var newPoints = new List();
100 |
101 | markers[first.Value] = markers[last.Value] = 1;
102 |
103 | while (last != null)
104 | {
105 | var maxSqDist = 0.0d;
106 |
107 | for (int? i = first + 1; i < last; i++)
108 | {
109 | var sqDist = GetSquareSegmentDistance(points[i.Value], points[first.Value], points[last.Value]);
110 |
111 | if (sqDist > maxSqDist)
112 | {
113 | index = i;
114 | maxSqDist = sqDist;
115 | }
116 | }
117 |
118 | if (maxSqDist > sqTolerance)
119 | {
120 | markers[index.Value] = 1;
121 | stack.AddRange(new[] { first, index, index, last });
122 | }
123 |
124 |
125 | if (stack.Count > 0)
126 | {
127 | last = stack[stack.Count - 1];
128 | stack.RemoveAt(stack.Count - 1);
129 | }
130 | else
131 | last = null;
132 |
133 | if (stack.Count > 0)
134 | {
135 | first = stack[stack.Count - 1];
136 | stack.RemoveAt(stack.Count - 1);
137 | }
138 | else
139 | first = null;
140 | }
141 |
142 | for (var i = 0; i < len; i++)
143 | {
144 | if (markers[i] != null)
145 | newPoints.Add(points[i]);
146 | }
147 |
148 | return newPoints;
149 | }
150 |
151 | ///
152 | /// Simplifies a list of points to a shorter list of points.
153 | ///
154 | /// Points original list of points
155 | /// Tolerance tolerance in the same measurement as the point coordinates
156 | /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm
157 | /// Simplified list of points
158 | public List Simplify(Point[] points, double tolerance = 0.3, bool highestQuality = false)
159 | {
160 | if(points == null || points.Length == 0)
161 | return new List();
162 |
163 | var sqTolerance = tolerance*tolerance;
164 |
165 | if (highestQuality)
166 | return SimplifyDouglasPeucker(points, sqTolerance);
167 |
168 | List points2 = SimplifyRadialDistance(points, sqTolerance);
169 | return SimplifyDouglasPeucker(points2.ToArray(), sqTolerance);
170 | }
171 |
172 | ///
173 | /// Simplifies a list of points to a shorter list of points.
174 | ///
175 | /// Points original list of points
176 | /// Tolerance tolerance in the same measurement as the point coordinates
177 | /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm
178 | /// Simplified list of points
179 | public static List SimplifyArray(Point[] points, double tolerance = 0.3, bool highestQuality = false)
180 | {
181 | return new SimplifyUtility().Simplify(points, tolerance, highestQuality);
182 | }
183 | }
184 | }
185 |
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/SimplifyUtility3D.cs:
--------------------------------------------------------------------------------
1 | // High-performance polyline simplification library
2 | //
3 | // This is a port of simplify-js by Vladimir Agafonkin, Copyright (c) 2012
4 | // https://github.com/mourner/simplify-js
5 | //
6 | // The code is ported from JavaScript to C#.
7 | // The library is created as portable and
8 | // is targeting multiple Microsoft plattforms.
9 | //
10 | // This library was ported by imshz @ http://www.shz.no
11 | // https://github.com/imshz/simplify-net
12 | //
13 | // This code is provided as is by the author. For complete license please
14 | // read the original license at https://github.com/mourner/simplify-js
15 |
16 | using System.Collections.Generic;
17 |
18 | namespace Simplifynet
19 | {
20 | ///
21 | /// Simplification of a 3D-polyline.
22 | /// Use only the 3D version if your point contains altitude information, if no altitude information is provided the 2D library gives a 20% performance gain.
23 | ///
24 | public class SimplifyUtility3D : ISimplifyUtility
25 | {
26 | // square distance between 2 points
27 | private double GetSquareDistance(Point p1, Point p2)
28 | {
29 | double dx = p1.X - p2.X,
30 | dy = p1.Y - p2.Y,
31 | dz = p1.Z - p2.Z;
32 |
33 | return (dx*dx) + (dy*dy) + (dz*dz);
34 | }
35 |
36 | // square distance from a point to a segment
37 | private double GetSquareSegmentDistance(Point p, Point p1, Point p2)
38 | {
39 | var x = p1.X;
40 | var y = p1.Y;
41 | var z = p1.Z;
42 | var dx = p2.X - x;
43 | var dy = p2.Y - y;
44 | var dz = p2.Z - z;
45 |
46 | if (!dx.Equals(0.0) || !dy.Equals(0.0) || !dz.Equals(0.0))
47 | {
48 | var t = ((p.X - x) * dx + (p.Y - y) * dy + (p.Z - z) * dz) / (dx * dx + dy * dy + dz * dz);
49 |
50 | if (t > 1)
51 | {
52 | x = p2.X;
53 | y = p2.Y;
54 | z = p2.Z;
55 | }
56 | else if (t > 0)
57 | {
58 | x += dx*t;
59 | y += dy*t;
60 | z += dz*t;
61 | }
62 | }
63 |
64 | dx = p.X - x;
65 | dy = p.Y - y;
66 | dz = p.Z - z;
67 |
68 | return (dx*dx) + (dy*dy) + (dz * dz);
69 | }
70 |
71 | // rest of the code doesn't care about point format
72 |
73 | // basic distance-based simplification
74 | private List SimplifyRadialDistance(Point[] points, double sqTolerance)
75 | {
76 | var prevPoint = points[0];
77 | var newPoints = new List {prevPoint};
78 | Point point = null;
79 |
80 | for (var i = 1; i < points.Length; i++)
81 | {
82 | point = points[i];
83 |
84 | if (GetSquareDistance(point, prevPoint) > sqTolerance)
85 | {
86 | newPoints.Add(point);
87 | prevPoint = point;
88 | }
89 | }
90 |
91 | if (point != null && !prevPoint.Equals(point))
92 | newPoints.Add(point);
93 |
94 | return newPoints;
95 | }
96 |
97 | // simplification using optimized Douglas-Peucker algorithm with recursion elimination
98 | private List SimplifyDouglasPeucker(Point[] points, double sqTolerance)
99 | {
100 | var len = points.Length;
101 | var markers = new int?[len];
102 | int? first = 0;
103 | int? last = len - 1;
104 | int? index = 0;
105 | var stack = new List();
106 | var newPoints = new List();
107 |
108 | markers[first.Value] = markers[last.Value] = 1;
109 |
110 | while (last != null)
111 | {
112 | var maxSqDist = 0.0d;
113 |
114 | for (int? i = first + 1; i < last; i++)
115 | {
116 | var sqDist = GetSquareSegmentDistance(points[i.Value], points[first.Value], points[last.Value]);
117 |
118 | if (sqDist > maxSqDist)
119 | {
120 | index = i;
121 | maxSqDist = sqDist;
122 | }
123 | }
124 |
125 | if (maxSqDist > sqTolerance)
126 | {
127 | markers[index.Value] = 1;
128 | stack.AddRange(new[] { first, index, index, last });
129 | }
130 |
131 |
132 | if (stack.Count > 0)
133 | {
134 | last = stack[stack.Count - 1];
135 | stack.RemoveAt(stack.Count - 1);
136 | }
137 | else
138 | last = null;
139 |
140 | if (stack.Count > 0)
141 | {
142 | first = stack[stack.Count - 1];
143 | stack.RemoveAt(stack.Count - 1);
144 | }
145 | else
146 | first = null;
147 | }
148 |
149 | for (var i = 0; i < len; i++)
150 | {
151 | if (markers[i] != null)
152 | newPoints.Add(points[i]);
153 | }
154 |
155 | return newPoints;
156 | }
157 |
158 | ///
159 | /// Simplifies a list of points to a shorter list of points.
160 | ///
161 | /// Points original list of points
162 | /// Tolerance tolerance in the same measurement as the point coordinates
163 | /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm
164 | /// Simplified list of points
165 | public List Simplify(Point[] points, double tolerance = 0.3, bool highestQuality = false)
166 | {
167 | if(points == null || points.Length == 0)
168 | return new List();
169 |
170 | var sqTolerance = tolerance*tolerance;
171 |
172 | if (!highestQuality)
173 | {
174 | List points2 = SimplifyRadialDistance(points, sqTolerance);
175 | return SimplifyDouglasPeucker(points2.ToArray(), sqTolerance);
176 | }
177 |
178 | return SimplifyDouglasPeucker(points, sqTolerance);
179 | }
180 |
181 | ///
182 | /// Simplifies a list of points to a shorter list of points.
183 | ///
184 | /// Points original list of points
185 | /// Tolerance tolerance in the same measurement as the point coordinates
186 | /// Enable highest quality for using Douglas-Peucker, set false for Radial-Distance algorithm
187 | /// Simplified list of points
188 | public static List SimplifyArray(Point[] points, double tolerance = 0.3, bool highestQuality = false)
189 | {
190 | return new SimplifyUtility().Simplify(points, tolerance, highestQuality);
191 | }
192 | }
193 | }
194 |
--------------------------------------------------------------------------------
/src/Simplifynet.CSharpPortable/Simplifynet.CSharpPortable.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 11.0
6 | Debug
7 | AnyCPU
8 | {31F65DF8-4049-490B-91DB-0265D9C28D17}
9 | Library
10 | Properties
11 | Simplifynet
12 | Simplifynet
13 | v4.5
14 | Profile259
15 | en-US
16 | 512
17 | {786C830F-07A1-408B-BD7F-6EE04809D6DB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
18 |
19 |
20 | true
21 | full
22 | false
23 | bin\Debug\
24 | DEBUG;TRACE
25 | prompt
26 | 4
27 |
28 |
29 | pdbonly
30 | true
31 | bin\Release\
32 | TRACE
33 | prompt
34 | 4
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
54 |
--------------------------------------------------------------------------------
/src/Simplifynet.Tests/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("Simplifynet.Tests")]
9 | [assembly: AssemblyDescription("")]
10 | [assembly: AssemblyConfiguration("")]
11 | [assembly: AssemblyCompany("")]
12 | [assembly: AssemblyProduct("Simplifynet.Tests")]
13 | [assembly: AssemblyCopyright("Copyright © 2014")]
14 | [assembly: AssemblyTrademark("")]
15 | [assembly: AssemblyCulture("")]
16 |
17 | // Setting ComVisible to false makes the types in this assembly not visible
18 | // to COM components. If you need to access a type in this assembly from
19 | // COM, set the ComVisible attribute to true on that type.
20 | [assembly: ComVisible(false)]
21 |
22 | // The following GUID is for the ID of the typelib if this project is exposed to COM
23 | [assembly: Guid("439c34e4-cd19-4f7a-ba64-d7dbf564182b")]
24 |
25 | // Version information for an assembly consists of the following four values:
26 | //
27 | // Major Version
28 | // Minor Version
29 | // Build Number
30 | // Revision
31 | //
32 | // You can specify all the values or you can default the Build and Revision Numbers
33 | // by using the '*' as shown below:
34 | // [assembly: AssemblyVersion("1.0.*")]
35 | [assembly: AssemblyVersion("1.0.0.0")]
36 | [assembly: AssemblyFileVersion("1.0.0.0")]
37 |
--------------------------------------------------------------------------------
/src/Simplifynet.Tests/SimplifyUtilityTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using NUnit.Framework;
4 | using SimplifyDotnet.Tests;
5 |
6 | namespace Simplifynet.Tests
7 | {
8 | [TestFixture]
9 | public class SimplifyUtilityTests
10 | {
11 | #region SimplifyTimings
12 |
13 | [Test]
14 | public void Simplify2DTimings()
15 | {
16 | SimplifyTimings(new SimplifyUtility());
17 | }
18 |
19 | [Test]
20 | public void Simplify3DTimings()
21 | {
22 | SimplifyTimings(new SimplifyUtility3D());
23 | }
24 |
25 | public void SimplifyTimings(ISimplifyUtility utility)
26 | {
27 | Stopwatch watch;
28 | var points = LongLine.GetPoints();
29 |
30 | watch = Stopwatch.StartNew();
31 | var firstRun3D = utility.Simplify(points);
32 | watch.Stop();
33 |
34 | Console.WriteLine("First time utility warmup took: " + watch.ElapsedTicks + " ticks");
35 |
36 | for (int i = 0; i < 10; i++)
37 | {
38 | const int times = 1000;
39 | long totalTime = 0;
40 |
41 | for (int b = 0; b < times; b++)
42 | {
43 | watch = Stopwatch.StartNew();
44 | var run = utility.Simplify(points);
45 | watch.Stop();
46 | totalTime += watch.ElapsedTicks;
47 | }
48 |
49 | Console.WriteLine(times + "x average time: " + (totalTime/times) + " ticks");
50 | }
51 | }
52 |
53 | #endregion
54 |
55 | #region SimplifyWithMultiplePointsShouldSimplfyCorrectly
56 |
57 | [Test]
58 | public void SimplifyWithMultiplePointsShouldSimplfyCorrectly()
59 | {
60 | SimplifyWithMultiplePointsShouldSimplfyCorrectly(new SimplifyUtility());
61 | }
62 |
63 | [Test]
64 | public void Simplify3DWithMultiplePointsShouldSimplfyCorrectly()
65 | {
66 | SimplifyWithMultiplePointsShouldSimplfyCorrectly(new SimplifyUtility3D());
67 | }
68 |
69 | public void SimplifyWithMultiplePointsShouldSimplfyCorrectly(ISimplifyUtility utility)
70 | {
71 | var points = new[] {
72 | new Point(224.55,250.15),new Point(226.91,244.19),new Point(233.31,241.45),new Point(234.98,236.06),
73 | new Point(244.21,232.76),new Point(262.59,215.31),new Point(267.76,213.81),new Point(273.57,201.84),
74 | new Point(273.12,192.16),new Point(277.62,189.03),new Point(280.36,181.41),new Point(286.51,177.74),
75 | new Point(292.41,159.37),new Point(296.91,155.64),new Point(314.95,151.37),new Point(319.75,145.16),
76 | new Point(330.33,137.57),new Point(341.48,139.96),new Point(369.98,137.89),new Point(387.39,142.51),
77 | new Point(391.28,139.39),new Point(409.52,141.14),new Point(414.82,139.75),new Point(427.72,127.30),
78 | new Point(439.60,119.74),new Point(474.93,107.87),new Point(486.51,106.75),new Point(489.20,109.45),
79 | new Point(493.79,108.63),new Point(504.74,119.66),new Point(512.96,122.35),new Point(518.63,120.89),
80 | new Point(524.09,126.88),new Point(529.57,127.86),new Point(534.21,140.93),new Point(539.27,147.24),
81 | new Point(567.69,148.91),new Point(575.25,157.26),new Point(580.62,158.15),new Point(601.53,156.85),
82 | new Point(617.74,159.86),new Point(622.00,167.04),new Point(629.55,194.60),new Point(638.90,195.61),
83 | new Point(641.26,200.81),new Point(651.77,204.56),new Point(671.55,222.55),new Point(683.68,217.45),
84 | new Point(695.25,219.15),new Point(700.64,217.98),new Point(703.12,214.36),new Point(712.26,215.87),
85 | new Point(721.49,212.81),new Point(727.81,213.36),new Point(729.98,208.73),new Point(735.32,208.20),
86 | new Point(739.94,204.77),new Point(769.98,208.42),new Point(779.60,216.87),new Point(784.20,218.16),
87 | new Point(800.24,214.62),new Point(810.53,219.73),new Point(817.19,226.82),new Point(820.77,236.17),
88 | new Point(827.23,236.16),new Point(829.89,239.89),new Point(851.00,248.94),new Point(859.88,255.49),
89 | new Point(865.21,268.53),new Point(857.95,280.30),new Point(865.48,291.45),new Point(866.81,298.66),
90 | new Point(864.68,302.71),new Point(867.79,306.17),new Point(859.87,311.37),new Point(860.08,314.35),
91 | new Point(858.29,314.94),new Point(858.10,327.60),new Point(854.54,335.40),new Point(860.92,343.00),
92 | new Point(856.43,350.15),new Point(851.42,352.96),new Point(849.84,359.59),new Point(854.56,365.53),
93 | new Point(849.74,370.38),new Point(844.09,371.89),new Point(844.75,380.44),new Point(841.52,383.67),
94 | new Point(839.57,390.40),new Point(845.59,399.05),new Point(848.40,407.55),new Point(843.71,411.30),
95 | new Point(844.09,419.88),new Point(839.51,432.76),new Point(841.33,441.04),new Point(847.62,449.22),
96 | new Point(847.16,458.44),new Point(851.38,462.79),new Point(853.97,471.15),new Point(866.36,480.77)
97 | };
98 |
99 | var simplified = new[] {
100 | new Point(224.55,250.15),new Point(267.76,213.81),new Point(296.91,155.64),new Point(330.33,137.57),
101 | new Point(409.52,141.14),new Point(439.60,119.74),new Point(486.51,106.75),new Point(529.57,127.86),
102 | new Point(539.27,147.24),new Point(617.74,159.86),new Point(629.55,194.60),new Point(671.55,222.55),
103 | new Point(727.81,213.36),new Point(739.94,204.77),new Point(769.98,208.42),new Point(779.60,216.87),
104 | new Point(800.24,214.62),new Point(820.77,236.17),new Point(859.88,255.49),new Point(865.21,268.53),
105 | new Point(857.95,280.30),new Point(867.79,306.17),new Point(859.87,311.37),new Point(854.54,335.40),
106 | new Point(860.92,343.00),new Point(849.84,359.59),new Point(854.56,365.53),new Point(844.09,371.89),
107 | new Point(839.57,390.40),new Point(848.40,407.55),new Point(839.51,432.76),new Point(853.97,471.15),
108 | new Point(866.36,480.77)};
109 |
110 | var result = utility.Simplify(points, 5, false);
111 |
112 | Assert.AreEqual(simplified.Length, result.Count);
113 | Assert.That(simplified, Is.EquivalentTo(result));
114 | }
115 |
116 | #endregion
117 |
118 | #region SimplifySinglePointResultSholdOnlyContainSinglePoint
119 |
120 | [Test]
121 | public void SimplifySinglePointResultSholdOnlyContainSinglePoint()
122 | {
123 | SimplifySinglePointResultSholdOnlyContainSinglePoint(new SimplifyUtility());
124 | }
125 |
126 | [Test]
127 | public void Simplify3DSinglePointResultSholdOnlyContainSinglePoint()
128 | {
129 | SimplifySinglePointResultSholdOnlyContainSinglePoint(new SimplifyUtility3D());
130 | }
131 |
132 | public void SimplifySinglePointResultSholdOnlyContainSinglePoint(ISimplifyUtility utility)
133 | {
134 | var Point = new Point(224.55, 250.15);
135 | var result = utility.Simplify(new[] { Point });
136 |
137 | Assert.AreEqual(1, result.Count);
138 | Assert.AreEqual(result[0].X, Point.X);
139 | Assert.AreEqual(result[0].Y, Point.Y);
140 | }
141 |
142 | #endregion
143 |
144 | #region SimplifyWithEmptyArraySholdShouldReturnEmptyList
145 |
146 | [Test]
147 | public void SimplifyWithEmptyArraySholdShouldReturnEmptyList()
148 | {
149 | SimplifyWithEmptyArraySholdShouldReturnEmptyList(new SimplifyUtility());
150 | }
151 |
152 | [Test]
153 | public void Simplify3DWithEmptyArraySholdShouldReturnEmptyList()
154 | {
155 | SimplifyWithEmptyArraySholdShouldReturnEmptyList(new SimplifyUtility3D());
156 | }
157 |
158 | public void SimplifyWithEmptyArraySholdShouldReturnEmptyList(ISimplifyUtility utility)
159 | {
160 | var result = utility.Simplify(new Point[0]);
161 |
162 | Assert.AreEqual(0, result.Count);
163 | }
164 |
165 | #endregion
166 | }
167 | }
--------------------------------------------------------------------------------
/src/Simplifynet.Tests/Simplifynet.Tests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Debug
6 | AnyCPU
7 | {D1CD5CE2-1833-4624-8A74-12854024FED2}
8 | Library
9 | Properties
10 | Simplifynet.Tests
11 | Simplifynet.Tests
12 | v4.5
13 | 512
14 |
15 |
16 | true
17 | full
18 | false
19 | bin\Debug\
20 | DEBUG;TRACE
21 | prompt
22 | 4
23 |
24 |
25 | pdbonly
26 | true
27 | bin\Release\
28 | TRACE
29 | prompt
30 | 4
31 |
32 |
33 |
34 | False
35 | extentions\nunit.framework.dll
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | {31F65DF8-4049-490B-91DB-0265D9C28D17}
56 | Simplifynet.CSharpPortable
57 |
58 |
59 |
60 |
67 |
--------------------------------------------------------------------------------
/src/Simplifynet.Tests/extentions/nunit.framework.dll:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/imshz/simplify-net/82d0c7289a17c29698562fe0687a9a4949ae2294/src/Simplifynet.Tests/extentions/nunit.framework.dll
--------------------------------------------------------------------------------
/src/Simplifynet.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 2013
4 | VisualStudioVersion = 12.0.30324.0
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Simplifynet.CSharpPortable", "Simplifynet.CSharpPortable\Simplifynet.CSharpPortable.csproj", "{31F65DF8-4049-490B-91DB-0265D9C28D17}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Simplifynet.Tests", "Simplifynet.Tests\Simplifynet.Tests.csproj", "{D1CD5CE2-1833-4624-8A74-12854024FED2}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {31F65DF8-4049-490B-91DB-0265D9C28D17}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {31F65DF8-4049-490B-91DB-0265D9C28D17}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {31F65DF8-4049-490B-91DB-0265D9C28D17}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {31F65DF8-4049-490B-91DB-0265D9C28D17}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {D1CD5CE2-1833-4624-8A74-12854024FED2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {D1CD5CE2-1833-4624-8A74-12854024FED2}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {D1CD5CE2-1833-4624-8A74-12854024FED2}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {D1CD5CE2-1833-4624-8A74-12854024FED2}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | EndGlobal
29 |
--------------------------------------------------------------------------------