/
wicket.src.js
378 lines (340 loc) · 15.7 KB
/
wicket.src.js
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
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
/*global console, document, window*/
/**
* @author K. Arthur Endsley <arthur.endsley@gmail.com>
*/
var Wkt = (function () { // Execute function immediately
return {
// The default delimiter for separating components of atomic geometry (coordinates)
delimiter: ' ',
isArray: function (obj) {
return !!(obj && obj.constructor == Array);
},
/**
* An object for reading WKT strings and writing geographic features
* @param {String} An optional WKT string for immediate read
* @param {Wkt.Wkt} A WKT object
*/
Wkt: function (initializer) {
var beginsWith, endsWith, trim;
/**
* @private
*/
beginsWith = function (str, sub) {
return str.substring(0, sub.length) === sub;
};
/**
* @private
*/
endsWith = function (str, sub) {
return str.substring(str.length - sub.length) === sub;
};
/**
* @private
*/
trim = function (str, sub) {
sub = sub || ' '; // Defaults to trimming spaces
// Trim beginning spaces
while (beginsWith(str, sub)) {
str = str.substring(1);
}
// Trim ending spaces
while (endsWith(str, sub)) {
str = str.substring(0, str.length - 1);
}
return str;
};
/**
* The default delimiter between X and Y coordinates.
*/
this.delimiter = Wkt.delimiter;
/**
* Some regular expressions copied from OpenLayers.Format.WKT.js
*/
this.regExes = {
'typeStr': /^\s*(\w+)\s*\(\s*(.*)\s*\)\s*$/,
'spaces': /\s+|\+/, // Matches the '+' or the empty space
'numeric': /-*\d+\.*\d+/,
'comma': /\s*,\s*/,
'parenComma': /\)\s*,\s*\(/,
'doubleParenComma': /\)\s*\)\s*,\s*\(\s*\(/,
'trimParens': /^\s*\(?(.*?)\)?\s*$/
};
/**
* Returns true if the internal geometry is a collection of geometries.
* @return {Boolean} Returns true when it is a collection
*/
this.isCollection = function () {
switch (this.type.slice(0, 5)) {
case 'multi':
// Trivial; any multi-geometry is a collection
return true;
case 'polyg':
// Polygons with holes are "collections" of rings
return true;
default:
// Any other geometry is not a collection
return false;
}
};
/**
* The internal representation of geometry--the "components" of geometry.
*/
this.components = undefined;
/**
* Sets internal geometry (components) from framework geometry (e.g.
* Google Polygon objects or google.maps.Polygon).
* @param obj {Object} The framework-dependent geometry representation
* @return {Wkt.Wkt} The object itself
*/
this.fromObject = function (obj) {
var result = this.deconstruct.call(this, obj);
this.components = result.components;
this.isRectangle = result.isRectangle || false;
this.type = result.type;
return this;
};
/**
* Creates external geometry objects based on a plug-in framework's
* construction methods and available geometry classes.
* @param config {Object} An optional framework-dependent properties specification
* @return {Object} The framework-dependent geometry representation
*/
this.toObject = function (config) {
return this.construct[this.type].call(this, config);
};
/**
* Reads a WKT string, validating and incorporating it.
* @param wkt {String} A WKT string
* @return {Array} An Array of internal geometry objects
*/
this.read = function (wkt) {
var matches;
matches = this.regExes.typeStr.exec(wkt);
if (matches) {
this.type = matches[1].toLowerCase();
this.base = matches[2];
if (this.ingest[this.type]) {
this.components = this.ingest[this.type].apply(this, [this.base]);
}
} else {
console.log("Invalid WKT string provided to read()");
throw {
name: "WKTError",
message: "Invalid WKT string provided to read()"
}
}
return this.components;
}; // eo readWkt
/**
* Writes a WKT string.
* @param components {Array} An Array of internal geometry objects
* @return {String} The corresponding WKT representation
*/
this.write = function (components) {
var i, pieces, data;
components = components || this.components;
pieces = [];
pieces.push(this.type.toUpperCase() + '(');
for (i = 0; i < components.length; i += 1) {
if (this.isCollection() && i > 0) {
pieces.push(',');
}
// There should be an extract function for the named type
if (!this.extract[this.type]) {
return null;
}
data = this.extract[this.type].apply(this, [components[i]]);
if (this.isCollection()) {
pieces.push('(' + data + ')');
} else {
pieces.push(data);
// If not at the end of the components, add a comma
if (i !== components.length - 1) {
pieces.push(',');
}
}
}
pieces.push(')');
return pieces.join('');
};
/**
* This object contains functions as property names that extract WKT
* strings from the internal representation.
*/
this.extract = {
/**
* Return a WKT string representing atomic (point) geometry
* @param point {Object} An object with x and y properties
* @return {String} The WKT representation
*/
'point': function (point) {
return point.x + this.delimiter + point.y;
},
/**
* Return a WKT string representing multiple atoms (points)
* @param point {Array} Multiple x-and-y objects
* @return {String} The WKT representation
*/
'multipoint': function (multipoint) {
var i, parts = [];
for (i = 0; i < multipoint.length; i += 1) {
parts.push(this.extract.point.apply(this, [multipoint[i]]));
}
return parts.join(',');
},
/**
* Return a WKT string representing a chain (linestring) of atoms
* @param point {Array} Multiple x-and-y objects
* @return {String} The WKT representation
*/
'linestring': function (linestring) {
// Extraction of linestrings is the same as for points
return this.extract.point.apply(this, [linestring]);
},
/**
* Return a WKT string representing multiple chains (multilinestring) of atoms
* @param point {Array} Multiple of multiple x-and-y objects
* @return {String} The WKT representation
*/
'multilinestring': function (multilinestring) {
var i, parts = [];
for (i = 0; i < multilinestring.length; i += 1) {
parts.push('(' + this.extract.linestring.apply(this, [multilinestring[i]]) + ')');
}
return parts.join(',');
},
/**
* Return a WKT string representing multiple atoms in closed series (polygon)
* @param point {Array} Collection of ordered x-and-y objects
* @return {String} The WKT representation
*/
'polygon': function (polygon) {
// Extraction of polygons is the same as for multipoints
return this.extract.multipoint.apply(this, [polygon]);
},
/**
* Return a WKT string representing multiple closed series (multipolygons) of multiple atoms
* @param point {Array} Collection of ordered x-and-y objects
* @return {String} The WKT representation
*/
'multipolygon': function (multipolygon) {
var i, parts = [];
for (i = 0; i < multipolygon.length; i += 1) {
parts.push('(' + this.extract.polygon.apply(this, [multipolygon[i]]) + ')');
}
return parts.join(',');
}
};
/**
* This object contains functions as property names that ingest WKT
* strings into the internal representation.
*/
this.ingest = {
/**
* Return point feature given a point WKT fragment.
* @param str {String} A WKT fragment representing the point
*/
'point': function (str) {
var coords = trim(str).split(this.regExes.spaces);
// In case a parenthetical group of coordinates is passed...
return [{ // ...Search for numeric substrings
x: parseFloat(this.regExes.numeric.exec(coords[0])[0]),
y: parseFloat(this.regExes.numeric.exec(coords[1])[0])
}];
},
/**
* Return a multipoint feature given a multipoint WKT fragment.
* @param str {String} A WKT fragment representing the multipoint
*/
'multipoint': function (str) {
var i, components, points;
components = [];
points = trim(str).split(this.regExes.comma);
for (i = 0; i < points.length; i += 1) {
components.push(this.ingest.point.apply(this, [points[i]]));
}
return components;
},
/**
* Return a linestring feature given a linestring WKT fragment.
* @param str {String} A WKT fragment representing the linestring
*/
'linestring': function (str) {
var i, multipoints, components;
// In our x-and-y representation of components, parsing
// multipoints is the same as parsing linestrings
multipoints = this.ingest.multipoint.apply(this, [str]);
// However, the points need to be joined
components = [];
for (i = 0; i < multipoints.length; i += 1) {
components = components.concat(multipoints[i]);
}
return components;
},
/**
* Return a multilinestring feature given a multilinestring WKT fragment.
* @param str {String} A WKT fragment representing the multilinestring
*/
'multilinestring': function (str) {
var i, components, line, lines;
components = [];
lines = trim(str).split(this.regExes.parenComma);
for (i = 0; i < lines.length; i += 1) {
line = lines[i].replace(this.regExes.trimParens, '$1');
components.push(this.ingest.linestring.apply(this, [line]));
}
return components;
},
/**
* Return a polygon feature given a polygon WKT fragment.
* @param str {String} A WKT fragment representing the polygon
*/
'polygon': function (str) {
var i, j, components, subcomponents, ring, rings;
rings = trim(str).split(this.regExes.parenComma);
components = []; // Holds one or more rings
for (i = 0; i < rings.length; i += 1) {
ring = rings[i].replace(this.regExes.trimParens, '$1').split(this.regExes.comma);
subcomponents = []; // Holds the outer ring and any inner rings (holes)
for (j = 0; j < ring.length; j += 1) {
// Split on the empty space or '+' character (between coordinates)
subcomponents.push({
x: parseFloat(ring[j].split(this.regExes.spaces)[0]),
y: parseFloat(ring[j].split(this.regExes.spaces)[1])
});
}
components.push(subcomponents);
}
return components;
},
/**
* Return a multipolygon feature given a multipolygon WKT fragment.
* @param str {String} A WKT fragment representing the multipolygon
*/
'multipolygon': function (str) {
var i, components, polygon, polygons;
components = [];
polygons = trim(str).split(this.regExes.doubleParenComma);
for (i = 0; i < polygons.length; i += 1) {
polygon = polygons[i].replace(this.regExes.trimParens, '$1');
components.push(this.ingest.polygon.apply(this, [polygon]));
}
return components;
},
/**
* Return an array of features given a geometrycollection WKT fragment.
* @param str {String} A WKT fragment representing the geometry collection
*/
'geometrycollection': function (str) {
console.log('The geometrycollection WKT type is not yet supported.');
}
}; // eo ingest
// An initial WKT string may be provided
if (initializer && typeof initializer === 'string') {
this.read(initializer);
} else if (this.fromGeometry) { // Or, an initial geometry object to be read
this.fromGeometry(initializer);
}
} // eo WKt.Wkt
}; // eo return
}()); // eo Wkt