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