Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: MSSQL Spatial driver
4 : * Purpose: Implements OGRMSSQLGeometryValidator class to create valid
5 : *SqlGeometries. Author: Tamas Szekeres, szekerest at gmail.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2010, Tamas Szekeres
9 : *
10 : * Permission is hereby granted, free of charge, to any person obtaining a
11 : * copy of this software and associated documentation files (the "Software"),
12 : * to deal in the Software without restriction, including without limitation
13 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 : * and/or sell copies of the Software, and to permit persons to whom the
15 : * Software is furnished to do so, subject to the following conditions:
16 : *
17 : * The above copyright notice and this permission notice shall be included
18 : * in all copies or substantial portions of the Software.
19 : *
20 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 : * DEALINGS IN THE SOFTWARE.
27 : ****************************************************************************/
28 :
29 : #include "cpl_conv.h"
30 : #include "ogr_mssqlspatial.h"
31 :
32 : /************************************************************************/
33 : /* OGRMSSQLGeometryValidator() */
34 : /************************************************************************/
35 :
36 0 : OGRMSSQLGeometryValidator::OGRMSSQLGeometryValidator(OGRGeometry *poGeom,
37 0 : int geomColumnType)
38 : {
39 0 : poOriginalGeometry = poGeom;
40 0 : poValidGeometry = nullptr;
41 0 : nGeomColumnType = geomColumnType;
42 0 : bIsValid = IsValid(poGeom);
43 0 : }
44 :
45 : /************************************************************************/
46 : /* ~OGRMSSQLGeometryValidator() */
47 : /************************************************************************/
48 :
49 0 : OGRMSSQLGeometryValidator::~OGRMSSQLGeometryValidator()
50 : {
51 0 : if (poValidGeometry)
52 0 : delete poValidGeometry;
53 0 : }
54 :
55 : /************************************************************************/
56 : /* IsValidLatLon() */
57 : /************************************************************************/
58 :
59 0 : static double MakeValidLatitude(double latitude)
60 : {
61 0 : if (latitude < -90)
62 0 : return -90;
63 :
64 0 : if (latitude > 90.0)
65 0 : return 90.0;
66 :
67 0 : return latitude;
68 : }
69 :
70 0 : static double MakeValidLongitude(double longitude)
71 : {
72 0 : if (longitude < -15069.0)
73 0 : return -15069.0;
74 :
75 0 : if (longitude > 15069.0)
76 0 : return 15069.0;
77 :
78 0 : return longitude;
79 : }
80 :
81 0 : bool OGRMSSQLGeometryValidator::IsValidLatLon(double longitude, double latitude)
82 : {
83 0 : if (MakeValidLatitude(latitude) != latitude)
84 : {
85 0 : if (poValidGeometry == nullptr)
86 0 : CPLError(CE_Warning, CPLE_NotSupported,
87 : "Latitude values must be between -90 and 90 degrees");
88 0 : return false;
89 : }
90 0 : if (MakeValidLongitude(longitude) != longitude)
91 : {
92 0 : if (poValidGeometry == nullptr)
93 0 : CPLError(
94 : CE_Warning, CPLE_NotSupported,
95 : "Longitude values must be between -15069 and 15069 degrees");
96 0 : return false;
97 : }
98 0 : return true;
99 : }
100 :
101 : /************************************************************************/
102 : /* IsValidCircularZ() */
103 : /************************************************************************/
104 :
105 0 : bool OGRMSSQLGeometryValidator::IsValidCircularZ(double z1, double z2)
106 : {
107 0 : if (z1 != z2)
108 : {
109 0 : if (poValidGeometry == nullptr)
110 0 : CPLError(CE_Warning, CPLE_NotSupported,
111 : "Circular arc segments with Z values must have equal Z "
112 : "value for all 3 points");
113 0 : return false;
114 : }
115 0 : return true;
116 : }
117 :
118 : /************************************************************************/
119 : /* IsValidPolygonRingCount() */
120 : /************************************************************************/
121 :
122 0 : bool OGRMSSQLGeometryValidator::IsValidPolygonRingCount(const OGRCurve *poGeom)
123 : {
124 0 : if (poGeom->getNumPoints() < 4)
125 : {
126 0 : if (poValidGeometry == nullptr)
127 0 : CPLError(
128 : CE_Warning, CPLE_NotSupported,
129 : "Each ring of a polygon must contain at least four points");
130 0 : return false;
131 : }
132 0 : return true;
133 : }
134 :
135 : /************************************************************************/
136 : /* IsValidPolygonRingClosed() */
137 : /************************************************************************/
138 :
139 0 : bool OGRMSSQLGeometryValidator::IsValidPolygonRingClosed(const OGRCurve *poGeom)
140 : {
141 0 : if (poGeom->get_IsClosed() == FALSE)
142 : {
143 0 : if (poValidGeometry == nullptr)
144 0 : CPLError(CE_Warning, CPLE_NotSupported,
145 : "Each ring of a polygon must have the same start and end "
146 : "points.");
147 0 : return false;
148 : }
149 0 : return true;
150 : }
151 :
152 : /************************************************************************/
153 : /* ValidatePoint() */
154 : /************************************************************************/
155 :
156 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRPoint *poGeom)
157 : {
158 0 : if (poGeom->IsEmpty())
159 0 : return true;
160 :
161 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
162 0 : return IsValidLatLon(poGeom->getX(), poGeom->getY());
163 :
164 0 : return true;
165 : }
166 :
167 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRPoint *poGeom)
168 : {
169 0 : if (poGeom->IsEmpty())
170 0 : return;
171 :
172 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
173 : {
174 0 : poGeom->setX(MakeValidLongitude(poGeom->getX()));
175 0 : poGeom->setY(MakeValidLatitude(poGeom->getY()));
176 : }
177 : }
178 :
179 : /************************************************************************/
180 : /* ValidateMultiPoint() */
181 : /************************************************************************/
182 :
183 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiPoint *poGeom)
184 : {
185 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
186 : {
187 0 : for (const auto point : *poGeom)
188 : {
189 0 : if (!IsValid(point))
190 0 : return false;
191 : }
192 : }
193 0 : return true;
194 : }
195 :
196 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiPoint *poGeom)
197 : {
198 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
199 : {
200 0 : for (auto point : *poGeom)
201 : {
202 0 : MakeValid(point);
203 : }
204 : }
205 0 : }
206 :
207 : /************************************************************************/
208 : /* ValidateSimpleCurve() */
209 : /************************************************************************/
210 :
211 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRSimpleCurve *poGeom)
212 : {
213 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
214 : {
215 0 : const int numPoints = poGeom->getNumPoints();
216 0 : for (int i = 0; i < numPoints; i++)
217 : {
218 0 : if (!IsValidLatLon(poGeom->getX(i), poGeom->getY(i)))
219 0 : return false;
220 : }
221 : }
222 0 : return true;
223 : }
224 :
225 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRSimpleCurve *poGeom)
226 : {
227 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
228 : {
229 0 : const int numPoints = poGeom->getNumPoints();
230 0 : for (int i = 0; i < numPoints; i++)
231 : {
232 0 : poGeom->setPoint(i, MakeValidLongitude(poGeom->getX(i)),
233 : MakeValidLatitude(poGeom->getY(i)));
234 : }
235 : }
236 0 : }
237 :
238 : /************************************************************************/
239 : /* ValidateCircularString() */
240 : /************************************************************************/
241 :
242 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCircularString *poGeom)
243 : {
244 0 : if (!IsValid(poGeom->toSimpleCurve()))
245 0 : return false;
246 :
247 0 : if (poGeom->Is3D())
248 : {
249 0 : const int numPoints = poGeom->getNumPoints();
250 0 : for (int i = 1; i < numPoints; i++)
251 : {
252 0 : if (!IsValidCircularZ(poGeom->getZ(i), poGeom->getZ(0)))
253 : {
254 0 : return false;
255 : }
256 : }
257 : }
258 0 : return true;
259 : }
260 :
261 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCircularString *poGeom)
262 : {
263 0 : MakeValid(poGeom->toSimpleCurve());
264 :
265 0 : if (poGeom->Is3D())
266 : {
267 0 : const int numPoints = poGeom->getNumPoints();
268 0 : for (int i = 1; i < numPoints; i++)
269 : {
270 0 : poGeom->setZ(i, poGeom->getZ(0));
271 : }
272 : }
273 0 : }
274 :
275 : /************************************************************************/
276 : /* ValidateCompoundCurve() */
277 : /************************************************************************/
278 :
279 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCompoundCurve *poGeom)
280 : {
281 0 : for (const auto poCurve : *poGeom)
282 : {
283 0 : switch (wkbFlatten(poCurve->getGeometryType()))
284 : {
285 0 : case wkbLineString:
286 0 : if (!IsValid(poCurve->toLineString()))
287 0 : return false;
288 0 : break;
289 :
290 0 : case wkbCircularString:
291 0 : if (!IsValid(poCurve->toCircularString()))
292 0 : return false;
293 0 : break;
294 :
295 0 : default:
296 0 : break;
297 : }
298 : }
299 0 : return true;
300 : }
301 :
302 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCompoundCurve *poGeom)
303 : {
304 0 : for (auto poCurve : *poGeom)
305 : {
306 0 : switch (wkbFlatten(poCurve->getGeometryType()))
307 : {
308 0 : case wkbLineString:
309 0 : MakeValid(poCurve->toLineString());
310 0 : break;
311 :
312 0 : case wkbCircularString:
313 0 : MakeValid(poCurve->toCircularString());
314 0 : break;
315 :
316 0 : default:
317 0 : break;
318 : }
319 : }
320 0 : }
321 :
322 : /************************************************************************/
323 : /* ValidateMultiLineString() */
324 : /************************************************************************/
325 :
326 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiLineString *poGeom)
327 : {
328 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
329 : {
330 0 : for (const auto part : *poGeom)
331 : {
332 0 : if (!IsValid(part))
333 0 : return false;
334 : }
335 : }
336 0 : return true;
337 : }
338 :
339 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiLineString *poGeom)
340 : {
341 0 : if (nGeomColumnType == MSSQLCOLTYPE_GEOGRAPHY)
342 : {
343 0 : for (auto part : *poGeom)
344 : {
345 0 : MakeValid(part);
346 : }
347 : }
348 0 : }
349 :
350 : /************************************************************************/
351 : /* ValidatePolygon() */
352 : /************************************************************************/
353 :
354 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRPolygon *poGeom)
355 : {
356 0 : OGRMSSQLGeometryValidator::MakeValid(poGeom->toCurvePolygon());
357 :
358 0 : poGeom->closeRings();
359 0 : }
360 :
361 : /************************************************************************/
362 : /* ValidateCurvePolygon() */
363 : /************************************************************************/
364 :
365 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRCurvePolygon *poGeom)
366 : {
367 0 : if (poGeom->IsEmpty())
368 0 : return true;
369 :
370 0 : for (const auto part : *poGeom)
371 : {
372 0 : if (!IsValid(part))
373 0 : return false;
374 :
375 0 : if (!IsValidPolygonRingCount(part))
376 0 : return false;
377 :
378 0 : if (!IsValidPolygonRingClosed(part))
379 0 : return false;
380 : }
381 :
382 0 : return true;
383 : }
384 :
385 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRCurvePolygon *poGeom)
386 : {
387 0 : if (poGeom->IsEmpty())
388 0 : return;
389 :
390 0 : for (auto part : *poGeom)
391 : {
392 0 : MakeValid(part);
393 : }
394 : }
395 :
396 : /************************************************************************/
397 : /* ValidateMultiPolygon() */
398 : /************************************************************************/
399 :
400 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRMultiPolygon *poGeom)
401 : {
402 0 : for (const auto part : *poGeom)
403 : {
404 0 : if (!IsValid(part))
405 0 : return false;
406 : }
407 0 : return true;
408 : }
409 :
410 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRMultiPolygon *poGeom)
411 : {
412 0 : for (auto part : *poGeom)
413 : {
414 0 : MakeValid(part);
415 : }
416 0 : }
417 :
418 : /************************************************************************/
419 : /* ValidateGeometryCollection() */
420 : /************************************************************************/
421 :
422 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRGeometryCollection *poGeom)
423 : {
424 0 : for (const auto part : *poGeom)
425 : {
426 0 : if (!IsValid(part))
427 0 : return false;
428 : }
429 0 : return true;
430 : }
431 :
432 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRGeometryCollection *poGeom)
433 : {
434 0 : for (auto part : *poGeom)
435 : {
436 0 : MakeValid(part);
437 : }
438 0 : }
439 :
440 : /************************************************************************/
441 : /* ValidateGeometry() */
442 : /************************************************************************/
443 :
444 0 : bool OGRMSSQLGeometryValidator::IsValid(const OGRGeometry *poGeom)
445 : {
446 0 : if (!poGeom)
447 0 : return false;
448 :
449 0 : switch (wkbFlatten(poGeom->getGeometryType()))
450 : {
451 0 : case wkbPoint:
452 0 : return IsValid(poGeom->toPoint());
453 0 : case wkbLineString:
454 0 : return IsValid(poGeom->toSimpleCurve());
455 0 : case wkbPolygon:
456 0 : return IsValid(poGeom->toPolygon());
457 0 : case wkbCurvePolygon:
458 0 : return IsValid(poGeom->toCurvePolygon());
459 0 : case wkbMultiPoint:
460 0 : return IsValid(poGeom->toMultiPoint());
461 0 : case wkbMultiLineString:
462 0 : return IsValid(poGeom->toMultiLineString());
463 0 : case wkbCircularString:
464 0 : return IsValid(poGeom->toCircularString());
465 0 : case wkbCompoundCurve:
466 0 : return IsValid(poGeom->toCompoundCurve());
467 0 : case wkbMultiPolygon:
468 0 : return IsValid(poGeom->toMultiPolygon());
469 0 : case wkbGeometryCollection:
470 0 : return IsValid(poGeom->toGeometryCollection());
471 0 : default:
472 0 : break;
473 : }
474 0 : return false;
475 : }
476 :
477 0 : void OGRMSSQLGeometryValidator::MakeValid(OGRGeometry *poGeom)
478 : {
479 0 : if (!poGeom)
480 0 : return;
481 :
482 0 : switch (wkbFlatten(poGeom->getGeometryType()))
483 : {
484 0 : case wkbPoint:
485 0 : MakeValid(poGeom->toPoint());
486 0 : break;
487 0 : case wkbLineString:
488 0 : MakeValid(poGeom->toSimpleCurve());
489 0 : break;
490 0 : case wkbPolygon:
491 0 : MakeValid(poGeom->toPolygon());
492 0 : break;
493 0 : case wkbCurvePolygon:
494 0 : MakeValid(poGeom->toCurvePolygon());
495 0 : break;
496 0 : case wkbMultiPoint:
497 0 : MakeValid(poGeom->toMultiPoint());
498 0 : break;
499 0 : case wkbMultiLineString:
500 0 : MakeValid(poGeom->toMultiLineString());
501 0 : break;
502 0 : case wkbCircularString:
503 0 : MakeValid(poGeom->toCircularString());
504 0 : break;
505 0 : case wkbCompoundCurve:
506 0 : MakeValid(poGeom->toCompoundCurve());
507 0 : break;
508 0 : case wkbMultiPolygon:
509 0 : MakeValid(poGeom->toMultiPolygon());
510 0 : break;
511 0 : case wkbGeometryCollection:
512 0 : MakeValid(poGeom->toGeometryCollection());
513 0 : break;
514 0 : default:
515 0 : break;
516 : }
517 : }
518 :
519 0 : bool OGRMSSQLGeometryValidator::ValidateGeometry(OGRGeometry *poGeom)
520 : {
521 0 : if (poValidGeometry != nullptr)
522 : {
523 0 : delete poValidGeometry;
524 0 : poValidGeometry = nullptr;
525 : }
526 :
527 0 : if (!IsValid(poGeom))
528 : {
529 0 : poValidGeometry = poGeom->clone();
530 0 : MakeValid(poValidGeometry);
531 0 : return false;
532 : }
533 0 : return true;
534 : }
535 :
536 : /************************************************************************/
537 : /* GetValidGeometryRef() */
538 : /************************************************************************/
539 0 : OGRGeometry *OGRMSSQLGeometryValidator::GetValidGeometryRef()
540 : {
541 0 : if (bIsValid || poOriginalGeometry == nullptr)
542 0 : return poOriginalGeometry;
543 :
544 0 : if (poValidGeometry)
545 : {
546 0 : CPLError(CE_Warning, CPLE_NotSupported,
547 : "Invalid geometry has been converted from %s to %s.",
548 0 : poOriginalGeometry->getGeometryName(),
549 0 : poValidGeometry->getGeometryName());
550 : }
551 : else
552 : {
553 0 : CPLError(CE_Warning, CPLE_NotSupported,
554 : "Invalid geometry has been converted from %s to null.",
555 0 : poOriginalGeometry->getGeometryName());
556 : }
557 :
558 0 : return poValidGeometry;
559 : }
|