Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: VFK Reader - Data block definition (SQLite)
4 : * Purpose: Implements VFKDataBlockSQLite
5 : * Author: Martin Landa, landa.martin gmail.com
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2012-2014, Martin Landa <landa.martin gmail.com>
9 : * Copyright (c) 2012-2014, Even Rouault <even dot rouault at spatialys.com>
10 : *
11 : * SPDX-License-Identifier: MIT
12 : ****************************************************************************/
13 :
14 : #include <algorithm>
15 : #include <limits>
16 : #include <map>
17 : #include <utility>
18 :
19 : #include "vfkreader.h"
20 : #include "vfkreaderp.h"
21 :
22 : #include "cpl_conv.h"
23 : #include "cpl_error.h"
24 :
25 : /*!
26 : \brief VFKDataBlockSQLite constructor
27 : */
28 1037 : VFKDataBlockSQLite::VFKDataBlockSQLite(const char *pszName,
29 1037 : const IVFKReader *poReader)
30 1037 : : IVFKDataBlock(pszName, poReader), m_hStmt(nullptr)
31 : {
32 1037 : }
33 :
34 : /*!
35 : \brief Load geometry (point layers)
36 :
37 : \return number of invalid features
38 : */
39 96 : int VFKDataBlockSQLite::LoadGeometryPoint()
40 : {
41 96 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
42 1 : return 0;
43 :
44 269 : const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
45 158 : EQUAL(m_pszName, "OP") ||
46 63 : EQUAL(m_pszName, "OBBP");
47 :
48 95 : CPLString osSQL;
49 : osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
50 95 : FID_COLUMN, m_pszName);
51 :
52 95 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
53 95 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
54 :
55 95 : if (poReader->IsSpatial())
56 95 : poReader->ExecuteSQL("BEGIN");
57 :
58 95 : int nGeometries = 0;
59 95 : int nInvalid = 0;
60 498 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
61 : {
62 : /* read values */
63 : const double x =
64 403 : -1.0 * sqlite3_column_double(
65 403 : hStmt, 0); /* S-JTSK coordinate system expected */
66 403 : const double y = -1.0 * sqlite3_column_double(hStmt, 1);
67 403 : const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
68 403 : const int rowId = sqlite3_column_int(hStmt, 3);
69 :
70 : VFKFeatureSQLite *poFeature =
71 403 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
72 403 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
73 : {
74 208 : continue;
75 : }
76 :
77 : /* create geometry */
78 403 : OGRPoint pt(x, y);
79 403 : if (!poFeature->SetGeometry(&pt))
80 : {
81 208 : nInvalid++;
82 208 : continue;
83 : }
84 :
85 : /* store also geometry in DB */
86 390 : if (poReader->IsSpatial() &&
87 195 : SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
88 195 : nGeometries++;
89 : }
90 :
91 : /* update number of geometries in VFK_DB_TABLE table */
92 95 : UpdateVfkBlocks(nGeometries);
93 :
94 95 : if (poReader->IsSpatial())
95 95 : poReader->ExecuteSQL("COMMIT");
96 :
97 95 : return bSkipInvalid ? 0 : nInvalid;
98 : }
99 :
100 : /*!
101 : \brief Set geometry for linestrings
102 :
103 : \param poLine VFK feature
104 : \param oOGRLine line geometry
105 : \param[in,out] bValid true when feature's geometry is valid
106 : \param ftype geometry VFK type
107 : \param[in,out] rowIdFeat list of row ids which forms linestring
108 : \param[in,out] nGeometries number of features with valid geometry
109 : */
110 210 : bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine,
111 : OGRLineString *oOGRLine,
112 : bool &bValid, const char *ftype,
113 : std::vector<int> &rowIdFeat,
114 : int &nGeometries)
115 : {
116 210 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
117 :
118 210 : oOGRLine->setCoordinateDimension(2); /* force 2D */
119 :
120 : /* check also VFK validity */
121 210 : if (bValid)
122 : {
123 : /* Feature types
124 :
125 : - '3' - line (2 points)
126 : - '4' - linestring (at least 2 points)
127 : - '11' - curve (at least 2 points)
128 : - '15' - circle (3 points)
129 : - '15 r' - circle (center point & radius)
130 : - '16' - arc (3 points)
131 : */
132 :
133 210 : const int npoints = oOGRLine->getNumPoints();
134 210 : if (EQUAL(ftype, "3") && npoints > 2)
135 : {
136 : /* be less pedantic, just inform user about data
137 : * inconsistency
138 :
139 : bValid = false;
140 : */
141 0 : CPLDebug("OGR-VFK",
142 : "Line (fid=" CPL_FRMT_GIB
143 : ") defined by more than two vertices",
144 : poLine->GetFID());
145 : }
146 210 : else if (EQUAL(ftype, "11") && npoints < 2)
147 : {
148 0 : bValid = false;
149 0 : CPLError(CE_Warning, CPLE_AppDefined,
150 : "Curve (fid=" CPL_FRMT_GIB
151 : ") defined by less than two vertices",
152 : poLine->GetFID());
153 : }
154 210 : else if (EQUAL(ftype, "15") && npoints != 3)
155 : {
156 0 : bValid = false;
157 0 : CPLError(CE_Warning, CPLE_AppDefined,
158 : "Circle (fid=" CPL_FRMT_GIB
159 : ") defined by invalid number of vertices (%d)",
160 0 : poLine->GetFID(), oOGRLine->getNumPoints());
161 : }
162 210 : else if (strlen(ftype) > 2 && STARTS_WITH_CI(ftype, "15") &&
163 : npoints != 1)
164 : {
165 0 : bValid = false;
166 0 : CPLError(CE_Warning, CPLE_AppDefined,
167 : "Circle (fid=" CPL_FRMT_GIB
168 : ") defined by invalid number of vertices (%d)",
169 0 : poLine->GetFID(), oOGRLine->getNumPoints());
170 : }
171 210 : else if (EQUAL(ftype, "16") && npoints != 3)
172 : {
173 0 : bValid = false;
174 0 : CPLError(CE_Warning, CPLE_AppDefined,
175 : "Arc (fid=" CPL_FRMT_GIB
176 : ") defined by invalid number of vertices (%d)",
177 0 : poLine->GetFID(), oOGRLine->getNumPoints());
178 : }
179 : }
180 :
181 : /* set geometry (NULL for invalid features) */
182 210 : if (bValid)
183 : {
184 210 : if (!poLine->SetGeometry(oOGRLine, ftype))
185 : {
186 0 : bValid = false;
187 : }
188 : }
189 : else
190 : {
191 0 : poLine->SetGeometry(nullptr);
192 : }
193 :
194 : /* update fid column */
195 210 : UpdateFID(poLine->GetFID(), rowIdFeat);
196 :
197 : /* store also geometry in DB */
198 210 : CPLAssert(!rowIdFeat.empty());
199 420 : if (bValid && poReader->IsSpatial() &&
200 210 : SaveGeometryToDB(poLine->GetGeometry(), rowIdFeat[0]) != OGRERR_FAILURE)
201 : {
202 210 : nGeometries++;
203 : }
204 :
205 210 : rowIdFeat.clear();
206 210 : oOGRLine->empty(); /* restore line */
207 :
208 210 : return bValid;
209 : }
210 :
211 : /*!
212 : \brief Load geometry (linestring SBP layer)
213 :
214 : \return number of invalid features
215 : */
216 16 : int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
217 : {
218 16 : int nInvalid = 0;
219 :
220 : VFKDataBlockSQLite *poDataBlockPoints =
221 16 : cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SOBR"));
222 16 : if (nullptr == poDataBlockPoints)
223 : {
224 0 : CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.\n",
225 : m_pszName);
226 0 : return nInvalid;
227 : }
228 :
229 16 : int nGeometries = 0;
230 16 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
231 :
232 16 : poDataBlockPoints->LoadGeometry();
233 :
234 16 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
235 1 : return 0;
236 :
237 15 : CPLString osSQL;
238 15 : osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
239 15 : poReader->ExecuteSQL(osSQL.c_str());
240 15 : bool bValid = true;
241 15 : int iIdx = 0;
242 :
243 15 : VFKFeatureSQLite *poLine = nullptr;
244 :
245 45 : for (int i = 0; i < 2; i++)
246 : {
247 : /* first collect linestrings related to HP, OB, DPM and ZVB
248 : then collect rest of linestrings */
249 30 : if (i == 0)
250 : osSQL.Printf(
251 : "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
252 : "FROM '%s' WHERE "
253 : "HP_ID IS NOT NULL OR OB_ID IS NOT NULL OR DPM_ID IS NOT NULL "
254 : "OR ZVB_ID IS NOT NULL "
255 : "ORDER BY HP_ID,OB_ID,DPM_ID,ZVB_ID,PORADOVE_CISLO_BODU",
256 15 : m_pszName);
257 : else
258 : osSQL.Printf(
259 : "SELECT BP_ID,PORADOVE_CISLO_BODU,PARAMETRY_SPOJENI,_rowid_ "
260 : "FROM '%s' WHERE "
261 : "OB_ID IS NULL AND HP_ID IS NULL AND DPM_ID IS NULL AND ZVB_ID "
262 : "IS NULL "
263 : "ORDER BY ID,PORADOVE_CISLO_BODU",
264 15 : m_pszName);
265 :
266 30 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
267 :
268 30 : if (poReader->IsSpatial())
269 30 : poReader->ExecuteSQL("BEGIN");
270 :
271 60 : std::vector<int> rowIdFeat;
272 60 : CPLString osFType;
273 60 : OGRLineString oOGRLine;
274 :
275 465 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
276 : {
277 : // read values
278 435 : const GUIntBig id = sqlite3_column_int64(hStmt, 0);
279 435 : const GUIntBig ipcb = sqlite3_column_int64(hStmt, 1);
280 : const char *pszFType =
281 435 : reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 2));
282 435 : int rowId = sqlite3_column_int(hStmt, 3);
283 :
284 435 : if (ipcb == 1)
285 : {
286 : VFKFeatureSQLite *poFeature =
287 210 : cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iIdx));
288 210 : if (poFeature == nullptr)
289 : {
290 0 : CPLError(CE_Failure, CPLE_AppDefined,
291 : "Cannot retrieve feature %d", iIdx);
292 0 : sqlite3_finalize(hStmt);
293 0 : break;
294 : }
295 210 : poFeature->SetRowId(rowId);
296 :
297 : /* set geometry & reset */
298 210 : if (poLine && !SetGeometryLineString(poLine, &oOGRLine, bValid,
299 : osFType.c_str(), rowIdFeat,
300 : nGeometries))
301 : {
302 0 : nInvalid++;
303 : }
304 :
305 210 : bValid = true;
306 210 : poLine = poFeature;
307 210 : osFType = pszFType ? pszFType : "";
308 210 : iIdx++;
309 : }
310 :
311 435 : VFKFeatureSQLite *poPoint = cpl::down_cast<VFKFeatureSQLite *>(
312 : poDataBlockPoints->GetFeature("ID", id));
313 435 : if (poPoint)
314 : {
315 435 : const OGRGeometry *pt = poPoint->GetGeometry();
316 435 : if (pt)
317 : {
318 435 : oOGRLine.addPoint(pt->toPoint());
319 : }
320 : else
321 : {
322 0 : CPLDebug("OGR-VFK",
323 : "Geometry (point ID = " CPL_FRMT_GUIB
324 : ") not valid",
325 : id);
326 0 : bValid = false;
327 : }
328 : }
329 : else
330 : {
331 0 : CPLDebug("OGR-VFK",
332 : "Point ID = " CPL_FRMT_GUIB " not found (rowid = %d)",
333 : id, rowId);
334 0 : bValid = false;
335 : }
336 :
337 : /* add vertex to the linestring */
338 435 : rowIdFeat.push_back(rowId);
339 : }
340 :
341 : /* add last line */
342 45 : if (poLine &&
343 15 : !SetGeometryLineString(poLine, &oOGRLine, bValid, osFType.c_str(),
344 : rowIdFeat, nGeometries))
345 : {
346 0 : nInvalid++;
347 : }
348 30 : poLine = nullptr;
349 :
350 30 : if (poReader->IsSpatial())
351 30 : poReader->ExecuteSQL("COMMIT");
352 : }
353 :
354 : /* update number of geometries in VFK_DB_TABLE table */
355 15 : UpdateVfkBlocks(nGeometries);
356 :
357 15 : return nInvalid;
358 : }
359 :
360 : /*!
361 : \brief Load geometry (linestring HP/DPM/ZVB layer)
362 :
363 : \return number of invalid features
364 : */
365 48 : int VFKDataBlockSQLite::LoadGeometryLineStringHP()
366 : {
367 48 : int nInvalid = 0;
368 48 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
369 :
370 : VFKDataBlockSQLite *poDataBlockLines =
371 48 : cpl::down_cast<VFKDataBlockSQLite *>(m_poReader->GetDataBlock("SBP"));
372 48 : if (nullptr == poDataBlockLines)
373 : {
374 0 : CPLError(CE_Failure, CPLE_FileIO, "Data block %s not found.",
375 : m_pszName);
376 0 : return nInvalid;
377 : }
378 :
379 48 : poDataBlockLines->LoadGeometry();
380 :
381 48 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
382 1 : return 0;
383 :
384 94 : CPLString osColumn;
385 47 : osColumn.Printf("%s_ID", m_pszName);
386 47 : const char *vrColumn[2] = {osColumn.c_str(), "PORADOVE_CISLO_BODU"};
387 :
388 47 : GUIntBig vrValue[2] = {0, 1}; // Reduce to first segment.
389 :
390 47 : CPLString osSQL;
391 47 : osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
392 : /* TODO: handle points in DPM */
393 47 : if (EQUAL(m_pszName, "DPM"))
394 16 : osSQL += " WHERE SOURADNICE_X IS NULL";
395 47 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
396 :
397 47 : if (poReader->IsSpatial())
398 47 : poReader->ExecuteSQL("BEGIN");
399 :
400 47 : int nGeometries = 0;
401 :
402 242 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
403 : {
404 : /* read values */
405 195 : vrValue[0] = sqlite3_column_int64(hStmt, 0);
406 195 : const GIntBig iFID = sqlite3_column_int64(hStmt, 1);
407 195 : const int rowId = sqlite3_column_int(hStmt, 2);
408 :
409 : VFKFeatureSQLite *poFeature =
410 195 : cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
411 195 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
412 : {
413 0 : continue;
414 : }
415 :
416 : VFKFeatureSQLite *poLine =
417 195 : poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
418 :
419 : const OGRGeometry *poOgrGeometry =
420 195 : poLine ? poLine->GetGeometry() : nullptr;
421 195 : if (!poOgrGeometry || !poFeature->SetGeometry(poOgrGeometry))
422 : {
423 0 : CPLDebug("OGR-VFK",
424 : "VFKDataBlockSQLite::LoadGeometryLineStringHP(): name=%s "
425 : "fid=" CPL_FRMT_GIB " "
426 : "id=" CPL_FRMT_GUIB " -> %s geometry",
427 : m_pszName, iFID, vrValue[0],
428 : poOgrGeometry ? "invalid" : "empty");
429 0 : nInvalid++;
430 0 : continue;
431 : }
432 :
433 : /* store also geometry in DB */
434 390 : if (poReader->IsSpatial() &&
435 195 : SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE)
436 195 : nGeometries++;
437 : }
438 :
439 : /* update number of geometries in VFK_DB_TABLE table */
440 47 : UpdateVfkBlocks(nGeometries);
441 :
442 47 : if (poReader->IsSpatial())
443 47 : poReader->ExecuteSQL("COMMIT");
444 :
445 47 : return nInvalid;
446 : }
447 :
448 : /*!
449 : \brief Load geometry (polygon BUD/PAR layers)
450 :
451 : \return number of invalid features
452 : */
453 32 : int VFKDataBlockSQLite::LoadGeometryPolygon()
454 : {
455 : #ifndef HAVE_GEOS
456 :
457 : CPLError(CE_Warning, CPLE_NotSupported,
458 : "GEOS support not enabled. Unable to build geometry for %s.",
459 : m_pszName);
460 : return -1;
461 :
462 : #else
463 :
464 32 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
465 :
466 32 : VFKDataBlockSQLite *poDataBlockLines1 = nullptr;
467 32 : VFKDataBlockSQLite *poDataBlockLines2 = nullptr;
468 32 : bool bIsPar = false;
469 32 : if (EQUAL(m_pszName, "PAR"))
470 : {
471 16 : poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
472 16 : m_poReader->GetDataBlock("HP"));
473 16 : poDataBlockLines2 = poDataBlockLines1;
474 16 : bIsPar = true;
475 : }
476 : else
477 : {
478 16 : poDataBlockLines1 = cpl::down_cast<VFKDataBlockSQLite *>(
479 16 : m_poReader->GetDataBlock("OB"));
480 16 : poDataBlockLines2 = cpl::down_cast<VFKDataBlockSQLite *>(
481 16 : m_poReader->GetDataBlock("SBP"));
482 16 : bIsPar = false;
483 : }
484 32 : if (nullptr == poDataBlockLines1)
485 : {
486 0 : CPLError(CE_Warning, CPLE_FileIO,
487 : "Data block %s not found. Unable to build geometry for %s.",
488 : bIsPar ? "HP" : "OB", m_pszName);
489 0 : return -1;
490 : }
491 32 : if (nullptr == poDataBlockLines2)
492 : {
493 0 : CPLError(CE_Warning, CPLE_FileIO,
494 : "Data block %s not found. Unable to build geometry for %s.",
495 : "SBP", m_pszName);
496 0 : return -1;
497 : }
498 :
499 32 : poDataBlockLines1->LoadGeometry();
500 32 : poDataBlockLines2->LoadGeometry();
501 :
502 32 : if (LoadGeometryFromDB()) // Try to load geometry from DB.
503 2 : return 0;
504 :
505 30 : const char *vrColumn[2] = {nullptr, nullptr};
506 30 : GUIntBig vrValue[2] = {0, 0};
507 30 : if (bIsPar)
508 : {
509 15 : vrColumn[0] = "PAR_ID_1";
510 15 : vrColumn[1] = "PAR_ID_2";
511 : }
512 : else
513 : {
514 15 : vrColumn[0] = "OB_ID";
515 15 : vrColumn[1] = "PORADOVE_CISLO_BODU";
516 15 : vrValue[1] = 1;
517 : }
518 :
519 30 : CPLString osSQL;
520 30 : osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
521 30 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
522 :
523 30 : if (poReader->IsSpatial())
524 30 : poReader->ExecuteSQL("BEGIN");
525 :
526 30 : int nInvalidNoLines = 0;
527 30 : int nInvalidNoRings = 0;
528 30 : int nGeometries = 0;
529 :
530 60 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
531 : {
532 : /* read values */
533 30 : const GUIntBig id = sqlite3_column_int64(hStmt, 0);
534 30 : const long iFID = static_cast<long>(sqlite3_column_int64(hStmt, 1));
535 30 : const int rowId = sqlite3_column_int(hStmt, 2);
536 :
537 : VFKFeatureSQLite *poFeature =
538 30 : cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
539 30 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
540 : {
541 0 : continue;
542 : }
543 :
544 : /* collect boundary lines */
545 30 : VFKFeatureSQLiteList poLineList;
546 30 : if (bIsPar)
547 : {
548 15 : vrValue[0] = vrValue[1] = id;
549 15 : poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
550 : }
551 : else
552 : {
553 : osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
554 15 : poDataBlockLines1->GetName(), id);
555 15 : if (poReader->IsSpatial())
556 : {
557 30 : CPLString osColumn;
558 :
559 15 : osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
560 15 : osSQL += osColumn;
561 : }
562 15 : sqlite3_stmt *hStmtOb = poReader->PrepareStatement(osSQL.c_str());
563 :
564 210 : while (poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE)
565 : {
566 195 : const GUIntBig idOb = sqlite3_column_int64(hStmtOb, 0);
567 195 : vrValue[0] = idOb;
568 : VFKFeatureSQLite *poLineSbp =
569 195 : poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
570 195 : if (poLineSbp)
571 195 : poLineList.push_back(poLineSbp);
572 : }
573 : }
574 30 : if (poLineList.empty())
575 : {
576 0 : CPLDebug("OGR-VFK", "%s: no lines to polygonize (fid = %ld)",
577 : m_pszName, iFID);
578 0 : nInvalidNoLines++;
579 0 : continue;
580 : }
581 :
582 30 : OGRMultiLineString oMultiLine;
583 420 : for (VFKFeatureSQLite *poLineFeature : poLineList)
584 : {
585 390 : const OGRGeometry *poLineGeom = poLineFeature->GetGeometry();
586 390 : if (poLineGeom)
587 : {
588 390 : oMultiLine.addGeometry(poLineGeom);
589 : }
590 : }
591 :
592 : /* polygonize using GEOSBuildArea() */
593 : auto poPolygonGeom =
594 30 : std::unique_ptr<OGRGeometry>(oMultiLine.BuildArea());
595 30 : if (!poPolygonGeom || poPolygonGeom->IsEmpty())
596 : {
597 0 : CPLDebug("OGR-VFK", "%s: unable to polygonize (fid = %ld)",
598 : m_pszName, iFID);
599 0 : nInvalidNoRings++;
600 0 : continue;
601 : }
602 :
603 : /* coerce to MultiPolygon if needed and check the geometry type */
604 30 : const auto eGeomType = wkbFlatten(poPolygonGeom->getGeometryType());
605 30 : if ((eGeomType == wkbPolygon) && (m_nGeometryType == wkbMultiPolygon))
606 : {
607 30 : auto poMP = std::make_unique<OGRMultiPolygon>();
608 15 : poMP->addGeometryDirectly(poPolygonGeom.release());
609 30 : poPolygonGeom = std::move(poMP);
610 : }
611 15 : else if (eGeomType != m_nGeometryType)
612 : {
613 0 : CPLDebug("OGR-VFK", "%s: invalid geometry type %d (fid = %ld)",
614 : m_pszName, (int)eGeomType, iFID);
615 0 : nInvalidNoRings++;
616 0 : continue;
617 : }
618 :
619 : /* set geometry */
620 30 : poPolygonGeom->setCoordinateDimension(2); /* force 2D */
621 30 : if (!poFeature->SetGeometry(poPolygonGeom.get()))
622 : {
623 0 : nInvalidNoRings++;
624 0 : continue;
625 : }
626 :
627 : /* store also geometry in DB */
628 60 : if (poReader->IsSpatial() &&
629 30 : SaveGeometryToDB(poPolygonGeom.get(), rowId) != OGRERR_FAILURE)
630 30 : nGeometries++;
631 : }
632 :
633 30 : CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", m_pszName,
634 : nInvalidNoLines, nInvalidNoRings);
635 :
636 : /* update number of geometries in VFK_DB_TABLE table */
637 30 : UpdateVfkBlocks(nGeometries);
638 :
639 30 : if (poReader->IsSpatial())
640 30 : poReader->ExecuteSQL("COMMIT");
641 :
642 30 : return nInvalidNoLines + nInvalidNoRings;
643 : #endif // HAVE_GEOS
644 : }
645 :
646 : /*!
647 : \brief Get feature by FID
648 :
649 : Modifies next feature id.
650 :
651 : \param nFID feature id
652 :
653 : \return pointer to feature definition or NULL on failure (not found)
654 : */
655 0 : IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID)
656 : {
657 0 : if (m_nFeatureCount < 0)
658 : {
659 0 : m_poReader->ReadDataRecords(this);
660 : }
661 :
662 0 : if (nFID < 1 || nFID > m_nFeatureCount)
663 0 : return nullptr;
664 :
665 0 : if (m_bGeometryPerBlock && !m_bGeometry)
666 : {
667 0 : LoadGeometry();
668 : }
669 :
670 0 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
671 :
672 0 : CPLString osSQL;
673 : osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, m_pszName,
674 0 : FID_COLUMN, nFID);
675 0 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
676 : {
677 0 : osSQL += " AND PORADOVE_CISLO_BODU = 1";
678 : }
679 0 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
680 :
681 0 : int rowId = -1;
682 0 : if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
683 : {
684 0 : rowId = sqlite3_column_int(hStmt, 0);
685 : }
686 0 : sqlite3_finalize(hStmt);
687 :
688 0 : return GetFeatureByIndex(rowId - 1);
689 : }
690 :
691 : /*!
692 : \brief Get first found feature based on its property
693 :
694 : \param column property name
695 : \param value property value
696 : \param bGeom True to check also geometry != NULL
697 :
698 : \return pointer to feature definition or NULL on failure (not found)
699 : */
700 435 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column,
701 : GUIntBig value, bool bGeom)
702 : {
703 435 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
704 :
705 870 : CPLString osSQL;
706 : osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, FID_COLUMN,
707 435 : m_pszName, column, value);
708 435 : if (bGeom)
709 : {
710 0 : CPLString osColumn;
711 :
712 0 : osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
713 0 : osSQL += osColumn;
714 : }
715 :
716 435 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
717 435 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
718 0 : return nullptr;
719 :
720 435 : const int idx = sqlite3_column_int(hStmt, 0) - 1;
721 435 : sqlite3_finalize(hStmt);
722 :
723 435 : if (idx < 0 || idx >= m_nFeatureCount) // ? assert
724 0 : return nullptr;
725 :
726 435 : return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
727 : }
728 :
729 : /*!
730 : \brief Get first found feature based on its properties (AND)
731 :
732 : \param column array of property names
733 : \param value array of property values
734 : \param num number of array items
735 : \param bGeom True to check also geometry != NULL
736 :
737 : \return pointer to feature definition or NULL on failure (not found)
738 : */
739 390 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column,
740 : GUIntBig *value, int num,
741 : bool bGeom)
742 : {
743 390 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
744 :
745 780 : CPLString osSQL;
746 390 : osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
747 :
748 780 : CPLString osItem;
749 1170 : for (int i = 0; i < num; i++)
750 : {
751 780 : if (i > 0)
752 390 : osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
753 : else
754 390 : osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
755 780 : osSQL += osItem;
756 : }
757 390 : if (bGeom)
758 : {
759 195 : osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
760 195 : osSQL += osItem;
761 : }
762 :
763 390 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
764 390 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
765 0 : return nullptr;
766 :
767 390 : int idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
768 390 : sqlite3_finalize(hStmt);
769 :
770 390 : if (idx < 0 || idx >= m_nFeatureCount) // ? assert
771 0 : return nullptr;
772 :
773 390 : return cpl::down_cast<VFKFeatureSQLite *>(GetFeatureByIndex(idx));
774 : }
775 :
776 : /*!
777 : \brief Get features based on properties
778 :
779 : \param column array of property names
780 : \param value array of property values
781 : \param num number of array items
782 :
783 : \return list of features
784 : */
785 15 : VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column,
786 : GUIntBig *value, int num)
787 : {
788 15 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
789 :
790 30 : CPLString osItem;
791 30 : CPLString osSQL;
792 15 : osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
793 45 : for (int i = 0; i < num; i++)
794 : {
795 30 : if (i > 0)
796 15 : osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
797 : else
798 15 : osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
799 30 : osSQL += osItem;
800 : }
801 15 : osSQL += " ORDER BY ";
802 15 : osSQL += FID_COLUMN;
803 :
804 30 : VFKFeatureSQLiteList fList;
805 :
806 15 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
807 210 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
808 : {
809 195 : const int iRowId = sqlite3_column_int(hStmt, 0);
810 : VFKFeatureSQLite *poFeature =
811 195 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iRowId - 1));
812 195 : if (poFeature == nullptr)
813 : {
814 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve feature %d",
815 : iRowId);
816 0 : sqlite3_finalize(hStmt);
817 0 : return VFKFeatureSQLiteList();
818 : }
819 195 : fList.push_back(poFeature);
820 : }
821 :
822 15 : return fList;
823 : }
824 :
825 : /*!
826 : \brief Save geometry to DB (as WKB)
827 :
828 : \param poGeom pointer to OGRGeometry to be saved
829 : \param iRowId row id to update
830 :
831 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
832 : */
833 630 : OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom,
834 : int iRowId)
835 : {
836 : int rc;
837 1260 : CPLString osSQL;
838 :
839 630 : sqlite3_stmt *hStmt = nullptr;
840 :
841 630 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
842 :
843 : /* check if geometry column exists (see SUPPRESS_GEOMETRY open
844 : option) */
845 630 : if (AddGeometryColumn() != OGRERR_NONE)
846 0 : return OGRERR_FAILURE;
847 :
848 630 : if (poGeom)
849 : {
850 630 : const size_t nWKBLen = poGeom->WkbSize();
851 630 : if (nWKBLen > static_cast<size_t>(std::numeric_limits<int>::max()))
852 : {
853 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
854 0 : return OGRERR_FAILURE;
855 : }
856 630 : GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(nWKBLen);
857 630 : if (pabyWKB)
858 : {
859 630 : poGeom->exportToWkb(wkbNDR, pabyWKB);
860 :
861 : osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", m_pszName,
862 630 : GEOM_COLUMN, iRowId);
863 630 : hStmt = poReader->PrepareStatement(osSQL.c_str());
864 :
865 630 : rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, static_cast<int>(nWKBLen),
866 : CPLFree);
867 630 : if (rc != SQLITE_OK)
868 : {
869 0 : sqlite3_finalize(hStmt);
870 0 : CPLError(CE_Failure, CPLE_AppDefined,
871 : "Storing geometry in DB failed");
872 0 : return OGRERR_FAILURE;
873 : }
874 : }
875 : }
876 : else
877 : { /* invalid */
878 : osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", m_pszName,
879 0 : GEOM_COLUMN, iRowId);
880 0 : hStmt = poReader->PrepareStatement(osSQL.c_str());
881 : }
882 :
883 630 : return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
884 : }
885 :
886 : /*!
887 : \brief Load geometry from DB
888 :
889 : \return true if geometry successfully loaded otherwise false
890 : */
891 192 : bool VFKDataBlockSQLite::LoadGeometryFromDB()
892 : {
893 192 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
894 :
895 192 : if (!poReader->IsSpatial()) /* check if DB is spatial */
896 0 : return false;
897 :
898 384 : CPLString osSQL;
899 : osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'",
900 192 : VFK_DB_TABLE, m_pszName);
901 192 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
902 192 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
903 0 : return false;
904 192 : const int nGeometries = sqlite3_column_int(hStmt, 0);
905 192 : sqlite3_finalize(hStmt);
906 :
907 192 : if (nGeometries < 1)
908 187 : return false;
909 :
910 15 : const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
911 10 : EQUAL(m_pszName, "OP") ||
912 5 : EQUAL(m_pszName, "OBBP");
913 :
914 : /* load geometry from DB */
915 : osSQL.Printf("SELECT %s,rowid,%s FROM %s ", GEOM_COLUMN, FID_COLUMN,
916 5 : m_pszName);
917 5 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
918 1 : osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
919 5 : osSQL += "ORDER BY ";
920 5 : osSQL += FID_COLUMN;
921 5 : hStmt = poReader->PrepareStatement(osSQL.c_str());
922 :
923 5 : int rowId = 0;
924 5 : int nInvalid = 0;
925 5 : int nGeometriesCount = 0;
926 :
927 47 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
928 : {
929 42 : rowId++; // =sqlite3_column_int(hStmt, 1);
930 42 : const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
931 : VFKFeatureSQLite *poFeature =
932 42 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
933 42 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
934 : {
935 0 : continue;
936 : }
937 :
938 : // read geometry from DB
939 42 : const int nBytes = sqlite3_column_bytes(hStmt, 0);
940 42 : OGRGeometry *poGeometry = nullptr;
941 42 : if (nBytes > 0 && OGRGeometryFactory::createFromWkb(
942 : sqlite3_column_blob(hStmt, 0), nullptr,
943 : &poGeometry, nBytes) == OGRERR_NONE)
944 : {
945 42 : nGeometriesCount++;
946 42 : if (!poFeature->SetGeometry(poGeometry))
947 : {
948 0 : nInvalid++;
949 : }
950 42 : delete poGeometry;
951 : }
952 : else
953 : {
954 0 : nInvalid++;
955 : }
956 : }
957 :
958 5 : CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", m_pszName,
959 : nGeometriesCount);
960 :
961 5 : if (nGeometriesCount != nGeometries)
962 : {
963 0 : CPLError(CE_Warning, CPLE_AppDefined,
964 : "%s: %d geometries loaded (should be %d)", m_pszName,
965 : nGeometriesCount, nGeometries);
966 : }
967 :
968 5 : if (nInvalid > 0 && !bSkipInvalid)
969 : {
970 0 : CPLError(CE_Warning, CPLE_AppDefined,
971 : "%s: %d features with invalid or empty geometry", m_pszName,
972 : nInvalid);
973 : }
974 :
975 5 : return true;
976 : }
977 :
978 : /*!
979 : \brief Update VFK_DB_TABLE table
980 :
981 : \param nGeometries number of geometries to update
982 : */
983 187 : void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries)
984 : {
985 374 : CPLString osSQL;
986 :
987 187 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
988 :
989 : /* update number of features in VFK_DB_TABLE table */
990 187 : const int nFeatCount = (int)GetFeatureCount();
991 187 : if (nFeatCount > 0)
992 : {
993 : osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'",
994 91 : VFK_DB_TABLE, nFeatCount, m_pszName);
995 91 : poReader->ExecuteSQL(osSQL.c_str());
996 : }
997 :
998 : /* update number of geometries in VFK_DB_TABLE table */
999 187 : if (nGeometries > 0)
1000 : {
1001 75 : CPLDebug("OGR-VFK",
1002 : "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> "
1003 : "%d geometries saved to internal DB",
1004 : m_pszName, nGeometries);
1005 :
1006 : osSQL.Printf(
1007 : "UPDATE %s SET num_geometries = %d WHERE table_name = '%s'",
1008 75 : VFK_DB_TABLE, nGeometries, m_pszName);
1009 75 : poReader->ExecuteSQL(osSQL.c_str());
1010 : }
1011 187 : }
1012 :
1013 : /*!
1014 : \brief Update feature id (see SBP)
1015 :
1016 : \param iFID feature id to set up
1017 : \param rowId list of rows to update
1018 : */
1019 210 : void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, const std::vector<int> &rowId)
1020 : {
1021 420 : CPLString osSQL, osValue;
1022 210 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1023 :
1024 : /* update number of geometries in VFK_DB_TABLE table */
1025 : osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (",
1026 210 : m_pszName, FID_COLUMN, iFID);
1027 645 : for (size_t i = 0; i < rowId.size(); i++)
1028 : {
1029 435 : if (i > 0)
1030 225 : osValue.Printf(",%d", rowId[i]);
1031 : else
1032 210 : osValue.Printf("%d", rowId[i]);
1033 435 : osSQL += osValue;
1034 : }
1035 210 : osSQL += ")";
1036 :
1037 210 : poReader->ExecuteSQL(osSQL.c_str());
1038 210 : }
1039 :
1040 : /*!
1041 : \brief Check is ring is closed
1042 :
1043 : \param poRing pointer to OGRLinearRing to check
1044 :
1045 : \return true if closed otherwise false
1046 : */
1047 0 : bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
1048 : {
1049 0 : const int nPoints = poRing->getNumPoints();
1050 0 : if (nPoints < 3)
1051 0 : return false;
1052 :
1053 0 : if (poRing->getX(0) == poRing->getX(nPoints - 1) &&
1054 0 : poRing->getY(0) == poRing->getY(nPoints - 1))
1055 0 : return true;
1056 :
1057 0 : return false;
1058 : }
1059 :
1060 : /*!
1061 : \brief Get primary key
1062 :
1063 : \return property name or NULL
1064 : */
1065 180 : const char *VFKDataBlockSQLite::GetKey() const
1066 : {
1067 180 : if (GetPropertyCount() > 1)
1068 : {
1069 180 : const VFKPropertyDefn *poPropDefn = GetProperty(0);
1070 180 : const char *pszKey = poPropDefn->GetName();
1071 180 : if (EQUAL(pszKey, "ID"))
1072 180 : return pszKey;
1073 : }
1074 :
1075 0 : return nullptr;
1076 : }
1077 :
1078 : /*!
1079 : \brief Get geometry SQL type (for geometry_columns table)
1080 :
1081 : \return geometry_type as integer
1082 : */
1083 976 : int VFKDataBlockSQLite::GetGeometrySQLType() const
1084 : {
1085 976 : if (m_nGeometryType == wkbMultiPolygon)
1086 15 : return 6;
1087 961 : else if (m_nGeometryType == wkbPolygon)
1088 15 : return 3;
1089 946 : else if (m_nGeometryType == wkbLineString)
1090 60 : return 2;
1091 886 : else if (m_nGeometryType == wkbPoint)
1092 90 : return 1;
1093 :
1094 796 : return 0; /* unknown geometry type */
1095 : }
1096 :
1097 : /*!
1098 : \brief Add geometry column into table if not exists
1099 :
1100 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1101 : */
1102 642 : OGRErr VFKDataBlockSQLite::AddGeometryColumn() const
1103 : {
1104 1284 : CPLString osSQL;
1105 :
1106 642 : VFKReaderSQLite *poReader = cpl::down_cast<VFKReaderSQLite *>(m_poReader);
1107 :
1108 642 : osSQL.Printf("SELECT %s FROM %s LIMIT 0", GEOM_COLUMN, m_pszName);
1109 642 : if (poReader->ExecuteSQL(osSQL.c_str(), CE_None) == OGRERR_FAILURE)
1110 : {
1111 : /* query failed, we assume that geometry column not exists */
1112 0 : osSQL.Printf("ALTER TABLE %s ADD COLUMN %s blob", m_pszName,
1113 0 : GEOM_COLUMN);
1114 0 : return poReader->ExecuteSQL(osSQL.c_str());
1115 : }
1116 :
1117 642 : return OGRERR_NONE;
1118 : }
1119 :
1120 : /*!
1121 : \brief Load feature properties
1122 :
1123 : Used for sequential access, see OGRVFKLayer:GetNextFeature().
1124 :
1125 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1126 : */
1127 5 : OGRErr VFKDataBlockSQLite::LoadProperties()
1128 : {
1129 10 : CPLString osSQL;
1130 :
1131 5 : if (m_hStmt)
1132 0 : sqlite3_finalize(m_hStmt);
1133 :
1134 : osSQL.Printf("SELECT * FROM %s", // TODO: where
1135 5 : m_pszName);
1136 5 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
1137 0 : osSQL += " WHERE PORADOVE_CISLO_BODU = 1";
1138 :
1139 5 : m_hStmt = cpl::down_cast<VFKReaderSQLite *>(m_poReader)
1140 5 : ->PrepareStatement(osSQL.c_str());
1141 :
1142 5 : if (m_hStmt == nullptr)
1143 0 : return OGRERR_FAILURE;
1144 :
1145 5 : return OGRERR_NONE;
1146 : }
1147 :
1148 : /*
1149 : \brief Clean feature properties for a next run
1150 :
1151 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1152 : */
1153 1040 : OGRErr VFKDataBlockSQLite::CleanProperties()
1154 : {
1155 1040 : if (m_hStmt)
1156 : {
1157 5 : if (sqlite3_finalize(m_hStmt) != SQLITE_OK)
1158 : {
1159 0 : m_hStmt = nullptr;
1160 0 : return OGRERR_FAILURE;
1161 : }
1162 5 : m_hStmt = nullptr;
1163 : }
1164 :
1165 1040 : return OGRERR_NONE;
1166 : }
|