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