0
# Validation Framework
1
2
The Elasticsearch Geo library provides a pluggable validation framework for ensuring geometric validity. The framework supports different validation strategies including standard geometric validation and geography-specific validation with latitude/longitude bounds checking.
3
4
## Capabilities
5
6
### GeometryValidator Interface
7
The core validation interface that defines the contract for geometry validation.
8
9
```java { .api }
10
/**
11
* Generic geometry validator interface for verifying geometry validity
12
*/
13
public interface GeometryValidator {
14
15
/**
16
* No-operation validator that performs no validation
17
*/
18
public static final GeometryValidator NOOP = (geometry) -> {};
19
20
/**
21
* Validates the geometry and throws IllegalArgumentException if invalid
22
* @param geometry the geometry to validate
23
* @throws IllegalArgumentException if geometry is not valid
24
*/
25
void validate(Geometry geometry);
26
}
27
28
// Usage pattern:
29
GeometryValidator validator = new SomeValidator();
30
try {
31
validator.validate(geometry);
32
// Geometry is valid
33
} catch (IllegalArgumentException e) {
34
// Geometry is invalid, handle error
35
System.err.println("Invalid geometry: " + e.getMessage());
36
}
37
```
38
39
### StandardValidator Implementation
40
Performs Z-coordinate validation based on the ignoreZValue parameter setting.
41
42
```java { .api }
43
/**
44
* Standard validator that checks Z-coordinate usage constraints
45
* - Validates that Z coordinates are only present when ignoreZValue is true
46
* - Ensures consistent Z-dimension handling across geometry processing
47
*/
48
public class StandardValidator implements GeometryValidator {
49
50
/**
51
* Gets a StandardValidator instance with specified Z-value handling
52
* @param ignoreZValue whether to allow Z coordinates in geometries
53
* @return validator instance (cached for performance)
54
*/
55
public static GeometryValidator instance(boolean ignoreZValue)
56
57
/**
58
* Validates geometry according to Z-coordinate constraints
59
* @param geometry the geometry to validate
60
* @throws IllegalArgumentException if geometry has Z values when ignoreZValue is false
61
*/
62
@Override
63
public void validate(Geometry geometry)
64
65
/**
66
* Checks individual Z coordinate values
67
* @param zValue the Z coordinate to check
68
* @throws IllegalArgumentException if Z value is present and ignoreZValue is false
69
*/
70
protected void checkZ(double zValue)
71
}
72
73
// Validation rules applied by StandardValidator:
74
// - When ignoreZValue is false: Throws exception if any geometry has Z coordinates
75
// - When ignoreZValue is true: Allows Z coordinates without validation
76
// - Uses visitor pattern to recursively check all geometry components
77
// - Provides static factory method for cached instances
78
```
79
80
### GeographyValidator Implementation
81
Validates geometries for geographic coordinate systems with latitude/longitude bounds checking.
82
83
```java { .api }
84
/**
85
* Geography-specific validator that checks coordinate bounds and geographic constraints
86
* - Validates latitude values are within [-90, 90] degrees
87
* - Validates longitude values are within [-180, 180] degrees
88
* - Validates Z-coordinate usage according to ignoreZValue setting
89
* - Applies standard validation rules plus geographic constraints
90
*/
91
public class GeographyValidator implements GeometryValidator {
92
93
/**
94
* Minimum longitude value (inclusive)
95
*/
96
private static final double MIN_LON_INCL = -180.0D;
97
98
/**
99
* Maximum longitude value (inclusive)
100
*/
101
private static final double MAX_LON_INCL = 180.0D;
102
103
/**
104
* Minimum latitude value (inclusive)
105
*/
106
private static final double MIN_LAT_INCL = -90.0D;
107
108
/**
109
* Maximum latitude value (inclusive)
110
*/
111
private static final double MAX_LAT_INCL = 90.0D;
112
113
/**
114
* Gets a GeographyValidator instance with specified Z-value handling
115
* @param ignoreZValue whether to allow Z coordinates in geometries
116
* @return validator instance (cached for performance)
117
*/
118
public static GeometryValidator instance(boolean ignoreZValue)
119
120
/**
121
* Validates geometry according to geographic coordinate constraints
122
* @param geometry the geometry to validate
123
* @throws IllegalArgumentException if geometry violates geographic constraints
124
*/
125
@Override
126
public void validate(Geometry geometry)
127
128
/**
129
* Validates latitude value is within standard bounds
130
* @param latitude the latitude value to check
131
* @throws IllegalArgumentException if latitude is outside [-90, 90] range
132
*/
133
protected void checkLatitude(double latitude)
134
135
/**
136
* Validates longitude value is within standard bounds
137
* @param longitude the longitude value to check
138
* @throws IllegalArgumentException if longitude is outside [-180, 180] range
139
*/
140
protected void checkLongitude(double longitude)
141
142
/**
143
* Validates altitude value according to ignoreZValue setting
144
* @param zValue the altitude value to check
145
* @throws IllegalArgumentException if altitude is present when ignoreZValue is false
146
*/
147
protected void checkAltitude(double zValue)
148
}
149
150
// Additional validation rules for GeographyValidator:
151
// - Latitude coordinates must be in range [-90.0, 90.0]
152
// - Longitude coordinates must be in range [-180.0, 180.0]
153
// - Altitude values should be reasonable (typically above -11000m, below 9000m)
154
// - Circle radius should be reasonable for geographic scales
155
// - Rectangle bounds should respect geographic coordinate limits
156
```
157
158
### No-Operation Validator
159
A validator that performs no validation, useful for performance-critical scenarios or trusted data.
160
161
```java { .api }
162
// Built-in no-op validator constant
163
GeometryValidator noopValidator = GeometryValidator.NOOP;
164
165
// No validation is performed - geometry is assumed to be valid
166
noopValidator.validate(anyGeometry); // Never throws exceptions
167
168
// Useful for:
169
// - Performance-critical applications where validation overhead is unacceptable
170
// - Processing trusted geometry data that is known to be valid
171
// - Testing scenarios where validation behavior should be bypassed
172
```
173
174
## Validation Integration
175
176
### WKT Parsing with Validation
177
The WKT parser integrates with the validation framework to ensure parsed geometries are valid.
178
179
```java { .api }
180
import org.elasticsearch.geometry.utils.WellKnownText;
181
182
// Parse WKT with validation
183
GeometryValidator validator = new StandardValidator();
184
String wkt = "POINT(-73.935242 40.730610)";
185
186
try {
187
Geometry geometry = WellKnownText.fromWKT(validator, false, wkt);
188
// Geometry is valid and ready to use
189
} catch (IllegalArgumentException e) {
190
// Invalid geometry or validation failure
191
System.err.println("Validation failed: " + e.getMessage());
192
} catch (IOException | ParseException e) {
193
// WKT parsing error
194
System.err.println("Parse error: " + e.getMessage());
195
}
196
197
// Parse with coercion and validation
198
try {
199
// Coercion attempts to fix minor issues (e.g., auto-close polygons)
200
Geometry coerced = WellKnownText.fromWKT(validator, true, wkt);
201
} catch (Exception e) {
202
System.err.println("Coercion and validation failed: " + e.getMessage());
203
}
204
```
205
206
### Custom Validator Implementation
207
Create custom validators for specific application requirements.
208
209
```java { .api }
210
/**
211
* Example custom validator that enforces application-specific constraints
212
*/
213
public class ApplicationValidator implements GeometryValidator {
214
215
private final double maxAllowedRadius;
216
private final Rectangle allowedBounds;
217
private final GeometryValidator baseValidator;
218
219
public ApplicationValidator(double maxRadius, Rectangle bounds) {
220
this.maxAllowedRadius = maxRadius;
221
this.allowedBounds = bounds;
222
this.baseValidator = new StandardValidator();
223
}
224
225
@Override
226
public void validate(Geometry geometry) {
227
// First apply standard validation
228
baseValidator.validate(geometry);
229
230
// Then apply custom rules using visitor pattern
231
geometry.visit(new GeometryVisitor<Void, IllegalArgumentException>() {
232
233
@Override
234
public Void visit(Circle circle) {
235
if (circle.getRadiusMeters() > maxAllowedRadius) {
236
throw new IllegalArgumentException(
237
"Circle radius " + circle.getRadiusMeters() +
238
" exceeds maximum allowed " + maxAllowedRadius);
239
}
240
validateWithinBounds(circle.getX(), circle.getY());
241
return null;
242
}
243
244
@Override
245
public Void visit(Point point) {
246
validateWithinBounds(point.getX(), point.getY());
247
return null;
248
}
249
250
@Override
251
public Void visit(Rectangle rectangle) {
252
validateWithinBounds(rectangle.getMinX(), rectangle.getMinY());
253
validateWithinBounds(rectangle.getMaxX(), rectangle.getMaxY());
254
return null;
255
}
256
257
// ... implement other visit methods
258
259
private void validateWithinBounds(double x, double y) {
260
if (x < allowedBounds.getMinX() || x > allowedBounds.getMaxX() ||
261
y < allowedBounds.getMinY() || y > allowedBounds.getMaxY()) {
262
throw new IllegalArgumentException(
263
"Coordinate (" + x + ", " + y + ") is outside allowed bounds");
264
}
265
}
266
});
267
}
268
}
269
```
270
271
## Validation Strategies
272
273
### Strict Validation Strategy
274
Use comprehensive validation for data integrity in critical applications.
275
276
```java { .api }
277
// Strict validation with geographic constraints
278
GeometryValidator strictValidator = new GeographyValidator();
279
280
// Validate all geometries before processing
281
public void processGeometry(Geometry geometry) {
282
strictValidator.validate(geometry);
283
284
// Proceed with processing - geometry is guaranteed valid
285
String wkt = WellKnownText.toWKT(geometry);
286
// ... additional processing
287
}
288
289
// Chain validators for multiple validation levels
290
public class ChainedValidator implements GeometryValidator {
291
private final List<GeometryValidator> validators;
292
293
public ChainedValidator(GeometryValidator... validators) {
294
this.validators = Arrays.asList(validators);
295
}
296
297
@Override
298
public void validate(Geometry geometry) {
299
for (GeometryValidator validator : validators) {
300
validator.validate(geometry);
301
}
302
}
303
}
304
305
// Use chained validation
306
GeometryValidator chainedValidator = new ChainedValidator(
307
StandardValidator.instance(true),
308
new GeographyValidator(),
309
new ApplicationValidator(10000.0, worldBounds)
310
);
311
```
312
313
### Performance-Optimized Strategy
314
Skip validation for performance-critical scenarios with trusted data.
315
316
```java { .api }
317
// No validation for maximum performance
318
GeometryValidator noValidation = GeometryValidator.NOOP;
319
320
// High-performance batch processing
321
public void processBatch(List<Geometry> geometries) {
322
for (Geometry geometry : geometries) {
323
// Skip validation for trusted data
324
noValidation.validate(geometry); // No-op
325
326
// Direct processing
327
processGeometryDirectly(geometry);
328
}
329
}
330
331
// Conditional validation based on data source
332
public void processGeometry(Geometry geometry, boolean trusted) {
333
GeometryValidator validator = trusted ?
334
GeometryValidator.NOOP :
335
StandardValidator.instance(true);
336
337
validator.validate(geometry);
338
// ... process geometry
339
}
340
```
341
342
## Usage Examples
343
344
### Basic Validation Example
345
346
```java
347
import org.elasticsearch.geometry.*;
348
import org.elasticsearch.geometry.utils.*;
349
350
// Create geometries
351
Point validPoint = new Point(-73.935242, 40.730610);
352
Point invalidLatPoint = new Point(-73.935242, 95.0); // Invalid latitude > 90
353
Circle negativeRadius = new Circle(-73.935242, 40.730610, -100.0); // Invalid negative radius
354
355
// Standard validation
356
GeometryValidator standardValidator = StandardValidator.instance(false);
357
358
try {
359
standardValidator.validate(validPoint); // Passes
360
System.out.println("Point is valid");
361
} catch (IllegalArgumentException e) {
362
System.err.println("Point validation failed: " + e.getMessage());
363
}
364
365
try {
366
standardValidator.validate(negativeRadius); // Fails - negative radius
367
} catch (IllegalArgumentException e) {
368
System.err.println("Circle validation failed: " + e.getMessage());
369
}
370
371
// Geography validation
372
GeometryValidator geoValidator = new GeographyValidator();
373
374
try {
375
geoValidator.validate(invalidLatPoint); // Fails - latitude out of bounds
376
} catch (IllegalArgumentException e) {
377
System.err.println("Geography validation failed: " + e.getMessage());
378
}
379
```
380
381
### WKT Parsing with Validation
382
383
```java
384
import org.elasticsearch.geometry.utils.WellKnownText;
385
386
GeometryValidator validator = StandardValidator.instance(true);
387
388
// Valid WKT
389
String validWkt = "POINT(-73.935242 40.730610)";
390
try {
391
Geometry geometry = WellKnownText.fromWKT(validator, false, validWkt);
392
System.out.println("Parsed valid geometry: " + geometry.type());
393
} catch (Exception e) {
394
System.err.println("Failed to parse: " + e.getMessage());
395
}
396
397
// Invalid WKT (unclosed polygon)
398
String invalidWkt = "POLYGON((-74.0 40.7,-73.9 40.7,-73.9 40.8))"; // Missing closing point
399
400
try {
401
// Without coercion - should fail
402
Geometry geometry = WellKnownText.fromWKT(validator, false, invalidWkt);
403
} catch (Exception e) {
404
System.err.println("Validation failed: " + e.getMessage());
405
}
406
407
try {
408
// With coercion - attempts to fix by auto-closing
409
Geometry geometry = WellKnownText.fromWKT(validator, true, invalidWkt);
410
System.out.println("Coerced geometry is valid");
411
} catch (Exception e) {
412
System.err.println("Coercion failed: " + e.getMessage());
413
}
414
```
415
416
### Custom Application Validator
417
418
```java
419
// Define application bounds (e.g., continental US)
420
Rectangle usBounds = new Rectangle(-125.0, -66.0, 49.0, 24.0);
421
422
// Create custom validator with application constraints
423
ApplicationValidator appValidator = new ApplicationValidator(50000.0, usBounds);
424
425
// Test geometries
426
Point validUSPoint = new Point(-100.0, 40.0); // Inside US bounds
427
Point invalidUSPoint = new Point(0.0, 0.0); // Outside US bounds (null island)
428
Circle largeCirle = new Circle(-100.0, 40.0, 100000.0); // Too large radius
429
430
try {
431
appValidator.validate(validUSPoint); // Passes
432
System.out.println("US point is valid");
433
} catch (IllegalArgumentException e) {
434
System.err.println("US validation failed: " + e.getMessage());
435
}
436
437
try {
438
appValidator.validate(invalidUSPoint); // Fails - outside bounds
439
} catch (IllegalArgumentException e) {
440
System.err.println("Point outside US bounds: " + e.getMessage());
441
}
442
443
try {
444
appValidator.validate(largeCirle); // Fails - radius too large
445
} catch (IllegalArgumentException e) {
446
System.err.println("Circle too large: " + e.getMessage());
447
}
448
```