forked from h2database/h2database
/
GeometryUtils.java
590 lines (512 loc) · 15.6 KB
/
GeometryUtils.java
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
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
/*
* Copyright 2004-2021 H2 Group. Multiple-Licensed under the MPL 2.0,
* and the EPL 1.0 (https://h2database.com/html/license.html).
* Initial Developer: H2 Group
*/
package org.h2.util.geometry;
/**
* Utilities for GEOMETRY data type.
*/
public final class GeometryUtils {
/**
* Converter output target.
*/
public abstract static class Target {
public Target() {
}
/**
* Initializes top-level target.
*
* @param srid
* SRID
*/
protected void init(int srid) {
}
/**
* Invoked to add dimension system requirement.
*
* @param dimensionSystem
* dimension system
*/
protected void dimensionSystem(int dimensionSystem) {
}
/**
* Invoked before writing a POINT.
*/
protected void startPoint() {
}
/**
* Invoked before writing a LINESTRING.
*
* @param numPoints
* number of points in line string
*/
protected void startLineString(int numPoints) {
}
/**
* Invoked before writing a POLYGON. If polygon is empty, both
* parameters are 0.
*
* @param numInner
* number of inner polygons
* @param numPoints
* number of points in outer polygon
*/
protected void startPolygon(int numInner, int numPoints) {
}
/**
* Invoked before writing an inner polygon in POLYGON.
*
* @param numInner
* number of points in inner polygon
*/
protected void startPolygonInner(int numInner) {
}
/**
* Invoked after writing of non-empty POLYGON.
*/
protected void endNonEmptyPolygon() {
}
/**
* Invoked before writing of a collection.
*
* @param type
* type of collection, one of
* {@link GeometryUtils#MULTI_POINT},
* {@link GeometryUtils#MULTI_LINE_STRING},
* {@link GeometryUtils#MULTI_POLYGON},
* {@link GeometryUtils#GEOMETRY_COLLECTION}
* @param numItems
* number of items in this collection
*/
protected void startCollection(int type, int numItems) {
}
/**
* Invoked before writing of a collection item.
*
* @param index
* 0-based index of this item in the collection
* @param total
* total number of items in the collection
* @return output target that should be used for processing of this
* collection item. May return this target or an custom
* sub-target.
*/
protected Target startCollectionItem(int index, int total) {
return this;
}
/**
* Invoked after writing of a collection item. This method is invoked on
* the same target that was used for
* {@link #startCollectionItem(int, int)}.
*
* @param target
* the result of {@link #startCollectionItem(int, int)}
* @param type
* type of collection
* @param index
* 0-based index of this item in the collection
* @param total
* total number of items in the collection
*/
protected void endCollectionItem(Target target, int type, int index, int total) {
}
/**
* Invoked after writing of the object.
*
* @param type
* type of the object
*/
protected void endObject(int type) {
}
/**
* Invoked to add a coordinate to a geometry.
*
* @param x
* X coordinate
* @param y
* Y coordinate
* @param z
* Z coordinate (NaN if not used)
* @param m
* M coordinate (NaN if not used)
* @param index
* 0-based index of coordinate in the current sequence
* @param total
* total number of coordinates in the current sequence
*/
protected abstract void addCoordinate(double x, double y, double z, double m, int index, int total);
}
/**
* Converter output target that calculates an envelope.
*/
public static final class EnvelopeTarget extends Target {
/**
* Enables or disables the envelope calculation. Inner rings of polygons
* are not counted.
*/
private boolean enabled;
/**
* Whether envelope was set.
*/
private boolean set;
private double minX, maxX, minY, maxY;
/**
* Creates a new envelope calculation target.
*/
public EnvelopeTarget() {
}
@Override
protected void startPoint() {
enabled = true;
}
@Override
protected void startLineString(int numPoints) {
enabled = true;
}
@Override
protected void startPolygon(int numInner, int numPoints) {
enabled = true;
}
@Override
protected void startPolygonInner(int numInner) {
enabled = false;
}
@Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
// POINT EMPTY has NaNs
if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) {
minX = maxX = x;
minY = maxY = y;
set = true;
} else {
if (minX > x) {
minX = x;
}
if (maxX < x) {
maxX = x;
}
if (minY > y) {
minY = y;
}
if (maxY < y) {
maxY = y;
}
}
}
}
/**
* Returns the envelope.
*
* @return the envelope, or null
*/
public double[] getEnvelope() {
return set ? new double[] { minX, maxX, minY, maxY } : null;
}
}
/**
* Converter output target that determines minimal dimension system for a
* geometry.
*/
public static final class DimensionSystemTarget extends Target {
private boolean hasZ;
private boolean hasM;
/**
* Creates a new dimension system determination target.
*/
public DimensionSystemTarget() {
}
@Override
protected void dimensionSystem(int dimensionSystem) {
if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) {
hasZ = true;
}
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
hasM = true;
}
}
@Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (!hasZ && !Double.isNaN(z)) {
hasZ = true;
}
if (!hasM && !Double.isNaN(m)) {
hasM = true;
}
}
/**
* Returns the minimal dimension system.
*
* @return the minimal dimension system
*/
public int getDimensionSystem() {
return (hasZ ? DIMENSION_SYSTEM_XYZ : 0) | (hasM ? DIMENSION_SYSTEM_XYM : 0);
}
}
/**
* Converter output target that calculates an envelope and determines the
* minimal dimension system.
*/
public static final class EnvelopeAndDimensionSystemTarget extends Target {
/**
* Enables or disables the envelope calculation. Inner rings of polygons
* are not counted.
*/
private boolean enabled;
/**
* Whether envelope was set.
*/
private boolean set;
private double minX, maxX, minY, maxY;
private boolean hasZ;
private boolean hasM;
/**
* Creates a new envelope and dimension system calculation target.
*/
public EnvelopeAndDimensionSystemTarget() {
}
@Override
protected void dimensionSystem(int dimensionSystem) {
if ((dimensionSystem & DIMENSION_SYSTEM_XYZ) != 0) {
hasZ = true;
}
if ((dimensionSystem & DIMENSION_SYSTEM_XYM) != 0) {
hasM = true;
}
}
@Override
protected void startPoint() {
enabled = true;
}
@Override
protected void startLineString(int numPoints) {
enabled = true;
}
@Override
protected void startPolygon(int numInner, int numPoints) {
enabled = true;
}
@Override
protected void startPolygonInner(int numInner) {
enabled = false;
}
@Override
protected void addCoordinate(double x, double y, double z, double m, int index, int total) {
if (!hasZ && !Double.isNaN(z)) {
hasZ = true;
}
if (!hasM && !Double.isNaN(m)) {
hasM = true;
}
// POINT EMPTY has NaNs
if (enabled && !Double.isNaN(x) && !Double.isNaN(y)) {
if (!set) {
minX = maxX = x;
minY = maxY = y;
set = true;
} else {
if (minX > x) {
minX = x;
}
if (maxX < x) {
maxX = x;
}
if (minY > y) {
minY = y;
}
if (maxY < y) {
maxY = y;
}
}
}
}
/**
* Returns the envelope.
*
* @return the envelope, or null
*/
public double[] getEnvelope() {
return set ? new double[] { minX, maxX, minY, maxY } : null;
}
/**
* Returns the minimal dimension system.
*
* @return the minimal dimension system
*/
public int getDimensionSystem() {
return (hasZ ? DIMENSION_SYSTEM_XYZ : 0) | (hasM ? DIMENSION_SYSTEM_XYM : 0);
}
}
/**
* POINT geometry type.
*/
public static final int POINT = 1;
/**
* LINESTRING geometry type.
*/
public static final int LINE_STRING = 2;
/**
* POLYGON geometry type.
*/
public static final int POLYGON = 3;
/**
* MULTIPOINT geometry type.
*/
public static final int MULTI_POINT = 4;
/**
* MULTILINESTRING geometry type.
*/
public static final int MULTI_LINE_STRING = 5;
/**
* MULTIPOLYGON geometry type.
*/
public static final int MULTI_POLYGON = 6;
/**
* GEOMETRYCOLLECTION geometry type.
*/
public static final int GEOMETRY_COLLECTION = 7;
/**
* Number of X coordinate.
*/
public static final int X = 0;
/**
* Number of Y coordinate.
*/
public static final int Y = 1;
/**
* Number of Z coordinate.
*/
public static final int Z = 2;
/**
* Number of M coordinate.
*/
public static final int M = 3;
/**
* Code of 2D (XY) dimension system.
*/
public static final int DIMENSION_SYSTEM_XY = 0;
/**
* Code of Z (XYZ) dimension system. Can also be used in bit masks to
* determine presence of dimension Z.
*/
public static final int DIMENSION_SYSTEM_XYZ = 1;
/**
* Code of M (XYM) dimension system. Can also be used in bit masks to
* determine presence of dimension M.
*/
public static final int DIMENSION_SYSTEM_XYM = 2;
/**
* Code of ZM (XYZM) dimension system. Can be also combined from
* {@link #DIMENSION_SYSTEM_XYZ} and {@link #DIMENSION_SYSTEM_XYM} using
* bitwise OR.
*/
public static final int DIMENSION_SYSTEM_XYZM = 3;
/**
* Minimum X coordinate index.
*/
public static final int MIN_X = 0;
/**
* Maximum X coordinate index.
*/
public static final int MAX_X = 1;
/**
* Minimum Y coordinate index.
*/
public static final int MIN_Y = 2;
/**
* Maximum Y coordinate index.
*/
public static final int MAX_Y = 3;
/**
* Calculates an envelope of a specified geometry.
*
* @param ewkb
* EWKB of a geometry
* @return envelope, or null
*/
public static double[] getEnvelope(byte[] ewkb) {
EnvelopeTarget target = new EnvelopeTarget();
EWKBUtils.parseEWKB(ewkb, target);
return target.getEnvelope();
}
/**
* Checks whether two envelopes intersect with each other.
*
* @param envelope1
* first envelope, or null
* @param envelope2
* second envelope, or null
* @return whether the specified envelopes intersects
*/
public static boolean intersects(double[] envelope1, double[] envelope2) {
return envelope1 != null && envelope2 != null //
&& envelope1[MAX_X] >= envelope2[MIN_X] //
&& envelope1[MIN_X] <= envelope2[MAX_X] //
&& envelope1[MAX_Y] >= envelope2[MIN_Y] //
&& envelope1[MIN_Y] <= envelope2[MAX_Y];
}
/**
* Returns union of two envelopes. This method does not modify the specified
* envelopes, but may return one of them as a result.
*
* @param envelope1
* first envelope, or null
* @param envelope2
* second envelope, or null
* @return union of two envelopes
*/
public static double[] union(double[] envelope1, double[] envelope2) {
if (envelope1 == null) {
return envelope2;
} else if (envelope2 == null) {
return envelope1;
}
double minX1 = envelope1[MIN_X], maxX1 = envelope1[MAX_X], minY1 = envelope1[MIN_Y], maxY1 = envelope1[MAX_Y];
double minX2 = envelope2[MIN_X], maxX2 = envelope2[MAX_X], minY2 = envelope2[MIN_Y], maxY2 = envelope2[MAX_Y];
boolean modified = false;
if (minX1 > minX2) {
minX1 = minX2;
modified = true;
}
if (maxX1 < maxX2) {
maxX1 = maxX2;
modified = true;
}
if (minY1 > minY2) {
minY1 = minY2;
modified = true;
}
if (maxY1 < maxY2) {
maxY1 = maxY2;
modified = true;
}
return modified ? new double[] { minX1, maxX1, minY1, maxY1 } : envelope1;
}
/**
* Normalizes all NaNs into single type on NaN and negative zero to positive
* zero.
*
* @param d
* double value
* @return normalized value
*/
static double toCanonicalDouble(double d) {
return Double.isNaN(d) ? Double.NaN : d == 0d ? 0d : d;
}
/**
* Throw exception if param is not finite value (ie. NaN/inf/etc)
*
* @param d
* a double value
* @return the same double value
*/
static double checkFinite(double d) {
if (!Double.isFinite(d)) {
throw new IllegalArgumentException();
}
return d;
}
private GeometryUtils() {
}
}