-
Notifications
You must be signed in to change notification settings - Fork 5
/
WkbTools.cs
217 lines (181 loc) · 7.41 KB
/
WkbTools.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.IO;
namespace SpatialTutorial
{
public enum WkbByteOrder : byte
{
Xdr = 0,
Ndr = 1
}
public enum WKBGeometryType : uint
{
Point = 1,
LineString = 2,
Polygon = 3,
MultiPoint = 4,
MultiLineString = 5,
MultiPolygon = 6,
GeometryCollection = 7
}
/// <summary> Converts Well-known Binary representations to a GraphicsPath instance. </summary>
public class WkbToGdi
{
#region public methods
public static GraphicsPath Parse(byte[] bytes, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
// Create a memory stream using the suppiled byte array.
using (MemoryStream ms = new MemoryStream(bytes))
{
// Create a new binary reader using the newly created memorystream.
using (BinaryReader reader = new BinaryReader(ms))
{
// Call the main create function.
return Parse(reader, geoToPixel);
}
}
}
public static GraphicsPath Parse(BinaryReader reader, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
// Get the first byte in the array. This specifies if the WKB is in
// XDR (big-endian) format of NDR (little-endian) format.
byte byteOrder = reader.ReadByte();
if (!Enum.IsDefined(typeof(WkbByteOrder), byteOrder))
{
throw new ArgumentException("Byte order not recognized");
}
// Get the type of this geometry.
uint type = (uint)ReadUInt32(reader, (WkbByteOrder)byteOrder);
if (!Enum.IsDefined(typeof(WKBGeometryType), type))
throw new ArgumentException("Geometry type not recognized");
switch ((WKBGeometryType)type)
{
case WKBGeometryType.Polygon:
return CreateWKBPolygon(reader, (WkbByteOrder)byteOrder, geoToPixel);
case WKBGeometryType.MultiPolygon:
return CreateWKBMultiPolygon(reader, (WkbByteOrder)byteOrder, geoToPixel);
default:
throw new NotSupportedException("Geometry type '" + type.ToString() + "' not supported");
}
}
#endregion
#region private methods
private static Point CreateWKBPoint(BinaryReader reader, WkbByteOrder byteOrder)
{
// Create and return the point.
return new Point((int)ReadDouble(reader, byteOrder), (int)ReadDouble(reader, byteOrder));
}
private static List<Point> ReadCoordinates(BinaryReader reader, WkbByteOrder byteOrder, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
// Get the number of points in this linestring.
int numPoints = (int)ReadUInt32(reader, byteOrder);
// Create a new array of coordinates.
var coords = new List<Point>();
Point p0 = new Point(0, 0 );
// Loop on the number of points in the ring.
for (int i = 0; i < numPoints; i++)
{
double x = ReadDouble(reader, byteOrder);
double y = ReadDouble(reader, byteOrder);
var dx = geoToPixel(new System.Windows.Point(x, y));
if(i == 0)
{
coords.Add(new Point(dx.X, dx.Y));
p0 = dx;
}
else if (i == numPoints-1)
{
if (Math.Abs(coords[0].X - dx.X) >= 1 || Math.Abs(coords[0].Y - dx.Y) >= 1)
coords.Add(new Point(dx.X, dx.Y));
}
else
{
if (Math.Abs(p0.X - dx.X) >= 1 || Math.Abs(p0.Y - dx.Y) >= 1)
{
// Add the coordinate.
coords.Add(new Point(dx.X, dx.Y));
p0 = dx;
}
}
}
return coords;
}
private static List<Point> CreateWKBLinearRing(BinaryReader reader, WkbByteOrder byteOrder, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
return ReadCoordinates(reader, byteOrder, geoToPixel);
}
private static GraphicsPath CreateWKBPolygon(BinaryReader reader, WkbByteOrder byteOrder, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
// Get the Number of rings in this Polygon.
int numRings = (int)ReadUInt32(reader, byteOrder);
Debug.Assert(numRings >= 1, "Number of rings in polygon must be 1 or more.");
GraphicsPath gp = new GraphicsPath();
var arr = CreateWKBLinearRing(reader, byteOrder, geoToPixel);
if (arr.Count > 2)
gp.AddPolygon(arr.ToArray());
// Create a new array of linearrings for the interior rings.
for (int i = 0; i < (numRings - 1); i++)
{
var rarr = CreateWKBLinearRing(reader, byteOrder, geoToPixel);
if (arr.Count > 2 && rarr.Count > 2)
{
gp.AddPolygon(rarr.ToArray());
}
}
// Create and return the Poylgon.
if (arr.Count > 2)
return gp;
else // don't return degenerated polygons (shrinked to a singularity)
return null;
}
private static GraphicsPath CreateWKBMultiPolygon(BinaryReader reader, WkbByteOrder byteOrder, Func<System.Windows.Point, System.Drawing.Point> geoToPixel)
{
GraphicsPath gp = new GraphicsPath();
// Get the number of Polygons.
int numPolygons = (int)ReadUInt32(reader, byteOrder);
// Loop on the number of polygons.
for (int i = 0; i < numPolygons; i++)
{
// read polygon header
reader.ReadByte();
ReadUInt32(reader, byteOrder);
var p = CreateWKBPolygon(reader, byteOrder, geoToPixel);
// TODO: Validate type
// Create the next polygon and add it to the array.
if(p != null)
gp.AddPath(p, false);
}
//Create and return the MultiPolygon.
if (gp.PointCount > 0)
return gp;
else
return null;
}
private static uint ReadUInt32(BinaryReader reader, WkbByteOrder byteOrder)
{
if (byteOrder == WkbByteOrder.Xdr)
{
byte[] bytes = BitConverter.GetBytes(reader.ReadUInt32());
Array.Reverse(bytes);
return BitConverter.ToUInt32(bytes, 0);
}
else
return reader.ReadUInt32();
}
private static double ReadDouble(BinaryReader reader, WkbByteOrder byteOrder)
{
if (byteOrder == WkbByteOrder.Xdr)
{
byte[] bytes = BitConverter.GetBytes(reader.ReadDouble());
Array.Reverse(bytes);
return BitConverter.ToDouble(bytes, 0);
}
else
return reader.ReadDouble();
}
#endregion
}
}