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 976 : VFKDataBlockSQLite::VFKDataBlockSQLite(const char *pszName,
29 976 : const IVFKReader *poReader)
30 976 : : IVFKDataBlock(pszName, poReader), m_hStmt(nullptr)
31 : {
32 976 : }
33 :
34 : /*!
35 : \brief Load geometry (point layers)
36 :
37 : \return number of invalid features
38 : */
39 90 : int VFKDataBlockSQLite::LoadGeometryPoint()
40 : {
41 90 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
42 1 : return 0;
43 :
44 252 : const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
45 148 : EQUAL(m_pszName, "OP") ||
46 59 : EQUAL(m_pszName, "OBBP");
47 :
48 89 : CPLString osSQL;
49 : osSQL.Printf("SELECT SOURADNICE_Y,SOURADNICE_X,%s,rowid FROM %s",
50 89 : FID_COLUMN, m_pszName);
51 :
52 89 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
53 89 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
54 :
55 89 : if (poReader->IsSpatial())
56 89 : poReader->ExecuteSQL("BEGIN");
57 :
58 89 : int nGeometries = 0;
59 89 : int nInvalid = 0;
60 271 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
61 : {
62 : /* read values */
63 : const double x =
64 182 : -1.0 * sqlite3_column_double(
65 182 : hStmt, 0); /* S-JTSK coordinate system expected */
66 182 : const double y = -1.0 * sqlite3_column_double(hStmt, 1);
67 182 : const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
68 182 : const int rowId = sqlite3_column_int(hStmt, 3);
69 :
70 : VFKFeatureSQLite *poFeature =
71 182 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
72 182 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
73 : {
74 0 : continue;
75 : }
76 :
77 : /* create geometry */
78 182 : OGRPoint pt(x, y);
79 182 : if (!poFeature->SetGeometry(&pt))
80 : {
81 0 : nInvalid++;
82 0 : continue;
83 : }
84 :
85 : /* store also geometry in DB */
86 364 : if (poReader->IsSpatial() &&
87 182 : SaveGeometryToDB(&pt, rowId) != OGRERR_FAILURE)
88 182 : nGeometries++;
89 : }
90 :
91 : /* update number of geometries in VFK_DB_TABLE table */
92 89 : UpdateVfkBlocks(nGeometries);
93 :
94 89 : if (poReader->IsSpatial())
95 89 : poReader->ExecuteSQL("COMMIT");
96 :
97 89 : 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 196 : bool VFKDataBlockSQLite::SetGeometryLineString(VFKFeatureSQLite *poLine,
111 : OGRLineString *oOGRLine,
112 : bool &bValid, const char *ftype,
113 : std::vector<int> &rowIdFeat,
114 : int &nGeometries)
115 : {
116 196 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
117 :
118 196 : oOGRLine->setCoordinateDimension(2); /* force 2D */
119 :
120 : /* check also VFK validity */
121 196 : 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 196 : const int npoints = oOGRLine->getNumPoints();
134 196 : 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 196 : 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 196 : 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 196 : 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 196 : 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 196 : if (bValid)
183 : {
184 196 : 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 196 : UpdateFID(poLine->GetFID(), rowIdFeat);
196 :
197 : /* store also geometry in DB */
198 196 : CPLAssert(!rowIdFeat.empty());
199 392 : if (bValid && poReader->IsSpatial() &&
200 196 : SaveGeometryToDB(poLine->GetGeometry(), rowIdFeat[0]) != OGRERR_FAILURE)
201 : {
202 196 : nGeometries++;
203 : }
204 :
205 196 : rowIdFeat.clear();
206 196 : oOGRLine->empty(); /* restore line */
207 :
208 196 : return bValid;
209 : }
210 :
211 : /*!
212 : \brief Load geometry (linestring SBP layer)
213 :
214 : \return number of invalid features
215 : */
216 15 : int VFKDataBlockSQLite::LoadGeometryLineStringSBP()
217 : {
218 15 : int nInvalid = 0;
219 :
220 : VFKDataBlockSQLite *poDataBlockPoints =
221 15 : (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SOBR");
222 15 : 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 15 : int nGeometries = 0;
230 15 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
231 :
232 15 : poDataBlockPoints->LoadGeometry();
233 :
234 15 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
235 1 : return 0;
236 :
237 14 : CPLString osSQL;
238 14 : osSQL.Printf("UPDATE %s SET %s = -1", m_pszName, FID_COLUMN);
239 14 : poReader->ExecuteSQL(osSQL.c_str());
240 14 : bool bValid = true;
241 14 : int iIdx = 0;
242 :
243 14 : VFKFeatureSQLite *poLine = nullptr;
244 :
245 42 : 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 28 : 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 14 : 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 14 : m_pszName);
265 :
266 28 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
267 :
268 28 : if (poReader->IsSpatial())
269 28 : poReader->ExecuteSQL("BEGIN");
270 :
271 56 : std::vector<int> rowIdFeat;
272 56 : CPLString osFType;
273 56 : OGRLineString oOGRLine;
274 :
275 434 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
276 : {
277 : // read values
278 406 : const GUIntBig id = sqlite3_column_int64(hStmt, 0);
279 406 : const GUIntBig ipcb = sqlite3_column_int64(hStmt, 1);
280 : const char *pszFType =
281 406 : reinterpret_cast<const char *>(sqlite3_column_text(hStmt, 2));
282 406 : int rowId = sqlite3_column_int(hStmt, 3);
283 :
284 406 : if (ipcb == 1)
285 : {
286 : VFKFeatureSQLite *poFeature =
287 196 : (VFKFeatureSQLite *)GetFeatureByIndex(iIdx);
288 196 : 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 196 : poFeature->SetRowId(rowId);
296 :
297 : /* set geometry & reset */
298 196 : if (poLine && !SetGeometryLineString(poLine, &oOGRLine, bValid,
299 : osFType.c_str(), rowIdFeat,
300 : nGeometries))
301 : {
302 0 : nInvalid++;
303 : }
304 :
305 196 : bValid = true;
306 196 : poLine = poFeature;
307 196 : osFType = pszFType ? pszFType : "";
308 196 : iIdx++;
309 : }
310 :
311 : VFKFeatureSQLite *poPoint =
312 406 : (VFKFeatureSQLite *)poDataBlockPoints->GetFeature("ID", id);
313 406 : if (poPoint)
314 : {
315 406 : const OGRGeometry *pt = poPoint->GetGeometry();
316 406 : if (pt)
317 : {
318 406 : 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 406 : rowIdFeat.push_back(rowId);
339 : }
340 :
341 : /* add last line */
342 42 : if (poLine &&
343 14 : !SetGeometryLineString(poLine, &oOGRLine, bValid, osFType.c_str(),
344 : rowIdFeat, nGeometries))
345 : {
346 0 : nInvalid++;
347 : }
348 28 : poLine = nullptr;
349 :
350 28 : if (poReader->IsSpatial())
351 28 : poReader->ExecuteSQL("COMMIT");
352 : }
353 :
354 : /* update number of geometries in VFK_DB_TABLE table */
355 14 : UpdateVfkBlocks(nGeometries);
356 :
357 14 : return nInvalid;
358 : }
359 :
360 : /*!
361 : \brief Load geometry (linestring HP/DPM/ZVB layer)
362 :
363 : \return number of invalid features
364 : */
365 45 : int VFKDataBlockSQLite::LoadGeometryLineStringHP()
366 : {
367 45 : int nInvalid = 0;
368 45 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
369 :
370 : VFKDataBlockSQLite *poDataBlockLines =
371 45 : (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SBP");
372 45 : 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 45 : poDataBlockLines->LoadGeometry();
380 :
381 45 : if (LoadGeometryFromDB()) /* try to load geometry from DB */
382 1 : return 0;
383 :
384 88 : CPLString osColumn;
385 44 : osColumn.Printf("%s_ID", m_pszName);
386 44 : const char *vrColumn[2] = {osColumn.c_str(), "PORADOVE_CISLO_BODU"};
387 :
388 44 : GUIntBig vrValue[2] = {0, 1}; // Reduce to first segment.
389 :
390 44 : CPLString osSQL;
391 44 : osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
392 : /* TODO: handle points in DPM */
393 44 : if (EQUAL(m_pszName, "DPM"))
394 15 : osSQL += " WHERE SOURADNICE_X IS NULL";
395 44 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
396 :
397 44 : if (poReader->IsSpatial())
398 44 : poReader->ExecuteSQL("BEGIN");
399 :
400 44 : int nGeometries = 0;
401 :
402 226 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
403 : {
404 : /* read values */
405 182 : vrValue[0] = sqlite3_column_int64(hStmt, 0);
406 182 : const GIntBig iFID = sqlite3_column_int64(hStmt, 1);
407 182 : const int rowId = sqlite3_column_int(hStmt, 2);
408 :
409 : VFKFeatureSQLite *poFeature =
410 182 : (VFKFeatureSQLite *)GetFeatureByIndex(rowId - 1);
411 182 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
412 : {
413 0 : continue;
414 : }
415 :
416 : VFKFeatureSQLite *poLine =
417 182 : poDataBlockLines->GetFeature(vrColumn, vrValue, 2, TRUE);
418 :
419 : const OGRGeometry *poOgrGeometry =
420 182 : poLine ? poLine->GetGeometry() : nullptr;
421 182 : 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 364 : if (poReader->IsSpatial() &&
435 182 : SaveGeometryToDB(poOgrGeometry, rowId) != OGRERR_FAILURE)
436 182 : nGeometries++;
437 : }
438 :
439 : /* update number of geometries in VFK_DB_TABLE table */
440 44 : UpdateVfkBlocks(nGeometries);
441 :
442 44 : if (poReader->IsSpatial())
443 44 : poReader->ExecuteSQL("COMMIT");
444 :
445 44 : return nInvalid;
446 : }
447 :
448 : /*!
449 : \brief Load geometry (polygon BUD/PAR layers)
450 :
451 : \return number of invalid features
452 : */
453 30 : 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 30 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
465 :
466 30 : VFKDataBlockSQLite *poDataBlockLines1 = nullptr;
467 30 : VFKDataBlockSQLite *poDataBlockLines2 = nullptr;
468 30 : bool bIsPar = false;
469 30 : if (EQUAL(m_pszName, "PAR"))
470 : {
471 : poDataBlockLines1 =
472 15 : (VFKDataBlockSQLite *)m_poReader->GetDataBlock("HP");
473 15 : poDataBlockLines2 = poDataBlockLines1;
474 15 : bIsPar = true;
475 : }
476 : else
477 : {
478 : poDataBlockLines1 =
479 15 : (VFKDataBlockSQLite *)m_poReader->GetDataBlock("OB");
480 : poDataBlockLines2 =
481 15 : (VFKDataBlockSQLite *)m_poReader->GetDataBlock("SBP");
482 15 : bIsPar = false;
483 : }
484 30 : 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 30 : 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 30 : poDataBlockLines1->LoadGeometry();
500 30 : poDataBlockLines2->LoadGeometry();
501 :
502 30 : if (LoadGeometryFromDB()) // Try to load geometry from DB.
503 1 : return 0;
504 :
505 29 : const char *vrColumn[2] = {nullptr, nullptr};
506 29 : GUIntBig vrValue[2] = {0, 0};
507 29 : if (bIsPar)
508 : {
509 14 : vrColumn[0] = "PAR_ID_1";
510 14 : 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 29 : CPLString osSQL;
520 29 : osSQL.Printf("SELECT ID,%s,rowid FROM %s", FID_COLUMN, m_pszName);
521 29 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
522 :
523 29 : if (poReader->IsSpatial())
524 29 : poReader->ExecuteSQL("BEGIN");
525 :
526 29 : int nInvalidNoLines = 0;
527 29 : int nInvalidNoRings = 0;
528 29 : int nGeometries = 0;
529 :
530 43 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
531 : {
532 : /* read values */
533 14 : const GUIntBig id = sqlite3_column_int64(hStmt, 0);
534 14 : const long iFID = static_cast<long>(sqlite3_column_int64(hStmt, 1));
535 14 : const int rowId = sqlite3_column_int(hStmt, 2);
536 :
537 : VFKFeatureSQLite *poFeature =
538 14 : (VFKFeatureSQLite *)GetFeatureByIndex(rowId - 1);
539 14 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
540 : {
541 0 : continue;
542 : }
543 :
544 : /* collect boundary lines */
545 14 : VFKFeatureSQLiteList poLineList;
546 14 : if (bIsPar)
547 : {
548 14 : vrValue[0] = vrValue[1] = id;
549 14 : poLineList = poDataBlockLines1->GetFeatures(vrColumn, vrValue, 2);
550 : }
551 : else
552 : {
553 : osSQL.Printf("SELECT ID FROM %s WHERE BUD_ID = " CPL_FRMT_GUIB,
554 0 : poDataBlockLines1->GetName(), id);
555 0 : if (poReader->IsSpatial())
556 : {
557 0 : CPLString osColumn;
558 :
559 0 : osColumn.Printf(" AND %s IS NULL", GEOM_COLUMN);
560 0 : osSQL += osColumn;
561 : }
562 0 : sqlite3_stmt *hStmtOb = poReader->PrepareStatement(osSQL.c_str());
563 :
564 0 : while (poReader->ExecuteSQL(hStmtOb) == OGRERR_NONE)
565 : {
566 0 : const GUIntBig idOb = sqlite3_column_int64(hStmtOb, 0);
567 0 : vrValue[0] = idOb;
568 : VFKFeatureSQLite *poLineSbp =
569 0 : poDataBlockLines2->GetFeature(vrColumn, vrValue, 2);
570 0 : if (poLineSbp)
571 0 : poLineList.push_back(poLineSbp);
572 : }
573 : }
574 14 : 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 14 : OGRMultiLineString oMultiLine;
583 196 : for (VFKFeatureSQLite *poLineFeature : poLineList)
584 : {
585 182 : const OGRGeometry *poLineGeom = poLineFeature->GetGeometry();
586 182 : if (poLineGeom)
587 : {
588 182 : oMultiLine.addGeometry(poLineGeom);
589 : }
590 : }
591 :
592 : /* polygonize using GEOSBuildArea() */
593 : auto poPolygonGeom =
594 14 : std::unique_ptr<OGRGeometry>(oMultiLine.BuildArea());
595 : /* only Polygons are allowed in VFK, in particular, no MultiPolygons */
596 28 : if (!poPolygonGeom || poPolygonGeom->IsEmpty() ||
597 14 : wkbFlatten(poPolygonGeom->getGeometryType()) != wkbPolygon)
598 : {
599 0 : CPLDebug("OGR-VFK", "%s: unable to polygonize (fid = %ld)",
600 : m_pszName, iFID);
601 0 : nInvalidNoRings++;
602 0 : continue;
603 : }
604 :
605 : /* set polygon */
606 14 : poPolygonGeom->setCoordinateDimension(2); /* force 2D */
607 14 : if (!poFeature->SetGeometry(poPolygonGeom.get()))
608 : {
609 0 : nInvalidNoRings++;
610 0 : continue;
611 : }
612 :
613 : /* store also geometry in DB */
614 28 : if (poReader->IsSpatial() &&
615 14 : SaveGeometryToDB(poPolygonGeom.get(), rowId) != OGRERR_FAILURE)
616 14 : nGeometries++;
617 : }
618 :
619 29 : CPLDebug("OGR-VFK", "%s: nolines = %d norings = %d", m_pszName,
620 : nInvalidNoLines, nInvalidNoRings);
621 :
622 : /* update number of geometries in VFK_DB_TABLE table */
623 29 : UpdateVfkBlocks(nGeometries);
624 :
625 29 : if (poReader->IsSpatial())
626 29 : poReader->ExecuteSQL("COMMIT");
627 :
628 29 : return nInvalidNoLines + nInvalidNoRings;
629 : #endif // HAVE_GEOS
630 : }
631 :
632 : /*!
633 : \brief Get feature by FID
634 :
635 : Modifies next feature id.
636 :
637 : \param nFID feature id
638 :
639 : \return pointer to feature definition or NULL on failure (not found)
640 : */
641 0 : IVFKFeature *VFKDataBlockSQLite::GetFeature(GIntBig nFID)
642 : {
643 0 : if (m_nFeatureCount < 0)
644 : {
645 0 : m_poReader->ReadDataRecords(this);
646 : }
647 :
648 0 : if (nFID < 1 || nFID > m_nFeatureCount)
649 0 : return nullptr;
650 :
651 0 : if (m_bGeometryPerBlock && !m_bGeometry)
652 : {
653 0 : LoadGeometry();
654 : }
655 :
656 0 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
657 :
658 0 : CPLString osSQL;
659 : osSQL.Printf("SELECT rowid FROM %s WHERE %s = " CPL_FRMT_GIB, m_pszName,
660 0 : FID_COLUMN, nFID);
661 0 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
662 : {
663 0 : osSQL += " AND PORADOVE_CISLO_BODU = 1";
664 : }
665 0 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
666 :
667 0 : int rowId = -1;
668 0 : if (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
669 : {
670 0 : rowId = sqlite3_column_int(hStmt, 0);
671 : }
672 0 : sqlite3_finalize(hStmt);
673 :
674 0 : return GetFeatureByIndex(rowId - 1);
675 : }
676 :
677 : /*!
678 : \brief Get first found feature based on its property
679 :
680 : \param column property name
681 : \param value property value
682 : \param bGeom True to check also geometry != NULL
683 :
684 : \return pointer to feature definition or NULL on failure (not found)
685 : */
686 406 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char *column,
687 : GUIntBig value, bool bGeom)
688 : {
689 406 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
690 :
691 812 : CPLString osSQL;
692 : osSQL.Printf("SELECT %s from %s WHERE %s = " CPL_FRMT_GUIB, FID_COLUMN,
693 406 : m_pszName, column, value);
694 406 : if (bGeom)
695 : {
696 0 : CPLString osColumn;
697 :
698 0 : osColumn.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
699 0 : osSQL += osColumn;
700 : }
701 :
702 406 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
703 406 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
704 0 : return nullptr;
705 :
706 406 : const int idx = sqlite3_column_int(hStmt, 0) - 1;
707 406 : sqlite3_finalize(hStmt);
708 :
709 406 : if (idx < 0 || idx >= m_nFeatureCount) // ? assert
710 0 : return nullptr;
711 :
712 406 : return (VFKFeatureSQLite *)GetFeatureByIndex(idx);
713 : }
714 :
715 : /*!
716 : \brief Get first found feature based on its properties (AND)
717 :
718 : \param column array of property names
719 : \param value array of property values
720 : \param num number of array items
721 : \param bGeom True to check also geometry != NULL
722 :
723 : \return pointer to feature definition or NULL on failure (not found)
724 : */
725 182 : VFKFeatureSQLite *VFKDataBlockSQLite::GetFeature(const char **column,
726 : GUIntBig *value, int num,
727 : bool bGeom)
728 : {
729 182 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
730 :
731 364 : CPLString osSQL;
732 182 : osSQL.Printf("SELECT %s FROM %s WHERE ", FID_COLUMN, m_pszName);
733 :
734 364 : CPLString osItem;
735 546 : for (int i = 0; i < num; i++)
736 : {
737 364 : if (i > 0)
738 182 : osItem.Printf(" AND %s = " CPL_FRMT_GUIB, column[i], value[i]);
739 : else
740 182 : osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
741 364 : osSQL += osItem;
742 : }
743 182 : if (bGeom)
744 : {
745 182 : osItem.Printf(" AND %s IS NOT NULL", GEOM_COLUMN);
746 182 : osSQL += osItem;
747 : }
748 :
749 182 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
750 182 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
751 0 : return nullptr;
752 :
753 182 : int idx = sqlite3_column_int(hStmt, 0) - 1; /* rowid starts at 1 */
754 182 : sqlite3_finalize(hStmt);
755 :
756 182 : if (idx < 0 || idx >= m_nFeatureCount) // ? assert
757 0 : return nullptr;
758 :
759 182 : return (VFKFeatureSQLite *)GetFeatureByIndex(idx);
760 : }
761 :
762 : /*!
763 : \brief Get features based on properties
764 :
765 : \param column array of property names
766 : \param value array of property values
767 : \param num number of array items
768 :
769 : \return list of features
770 : */
771 14 : VFKFeatureSQLiteList VFKDataBlockSQLite::GetFeatures(const char **column,
772 : GUIntBig *value, int num)
773 : {
774 14 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
775 :
776 28 : CPLString osItem;
777 28 : CPLString osSQL;
778 14 : osSQL.Printf("SELECT rowid from %s WHERE ", m_pszName);
779 42 : for (int i = 0; i < num; i++)
780 : {
781 28 : if (i > 0)
782 14 : osItem.Printf(" OR %s = " CPL_FRMT_GUIB, column[i], value[i]);
783 : else
784 14 : osItem.Printf("%s = " CPL_FRMT_GUIB, column[i], value[i]);
785 28 : osSQL += osItem;
786 : }
787 14 : osSQL += " ORDER BY ";
788 14 : osSQL += FID_COLUMN;
789 :
790 28 : VFKFeatureSQLiteList fList;
791 :
792 14 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
793 196 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
794 : {
795 182 : const int iRowId = sqlite3_column_int(hStmt, 0);
796 : VFKFeatureSQLite *poFeature =
797 182 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(iRowId - 1));
798 182 : if (poFeature == nullptr)
799 : {
800 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot retrieve feature %d",
801 : iRowId);
802 0 : sqlite3_finalize(hStmt);
803 0 : return VFKFeatureSQLiteList();
804 : }
805 182 : fList.push_back(poFeature);
806 : }
807 :
808 14 : return fList;
809 : }
810 :
811 : /*!
812 : \brief Save geometry to DB (as WKB)
813 :
814 : \param poGeom pointer to OGRGeometry to be saved
815 : \param iRowId row id to update
816 :
817 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
818 : */
819 574 : OGRErr VFKDataBlockSQLite::SaveGeometryToDB(const OGRGeometry *poGeom,
820 : int iRowId)
821 : {
822 : int rc;
823 1148 : CPLString osSQL;
824 :
825 574 : sqlite3_stmt *hStmt = nullptr;
826 :
827 574 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
828 :
829 : /* check if geometry column exists (see SUPPRESS_GEOMETRY open
830 : option) */
831 574 : if (AddGeometryColumn() != OGRERR_NONE)
832 0 : return OGRERR_FAILURE;
833 :
834 574 : if (poGeom)
835 : {
836 574 : const size_t nWKBLen = poGeom->WkbSize();
837 574 : if (nWKBLen > static_cast<size_t>(std::numeric_limits<int>::max()))
838 : {
839 0 : CPLError(CE_Failure, CPLE_AppDefined, "Too large geometry");
840 0 : return OGRERR_FAILURE;
841 : }
842 574 : GByte *pabyWKB = (GByte *)VSI_MALLOC_VERBOSE(nWKBLen);
843 574 : if (pabyWKB)
844 : {
845 574 : poGeom->exportToWkb(wkbNDR, pabyWKB);
846 :
847 : osSQL.Printf("UPDATE %s SET %s = ? WHERE rowid = %d", m_pszName,
848 574 : GEOM_COLUMN, iRowId);
849 574 : hStmt = poReader->PrepareStatement(osSQL.c_str());
850 :
851 574 : rc = sqlite3_bind_blob(hStmt, 1, pabyWKB, static_cast<int>(nWKBLen),
852 : CPLFree);
853 574 : if (rc != SQLITE_OK)
854 : {
855 0 : sqlite3_finalize(hStmt);
856 0 : CPLError(CE_Failure, CPLE_AppDefined,
857 : "Storing geometry in DB failed");
858 0 : return OGRERR_FAILURE;
859 : }
860 : }
861 : }
862 : else
863 : { /* invalid */
864 : osSQL.Printf("UPDATE %s SET %s = NULL WHERE rowid = %d", m_pszName,
865 0 : GEOM_COLUMN, iRowId);
866 0 : hStmt = poReader->PrepareStatement(osSQL.c_str());
867 : }
868 :
869 574 : return poReader->ExecuteSQL(hStmt); /* calls sqlite3_finalize() */
870 : }
871 :
872 : /*!
873 : \brief Load geometry from DB
874 :
875 : \return true if geometry successfully loaded otherwise false
876 : */
877 180 : bool VFKDataBlockSQLite::LoadGeometryFromDB()
878 : {
879 180 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
880 :
881 180 : if (!poReader->IsSpatial()) /* check if DB is spatial */
882 0 : return false;
883 :
884 360 : CPLString osSQL;
885 : osSQL.Printf("SELECT num_geometries FROM %s WHERE table_name = '%s'",
886 180 : VFK_DB_TABLE, m_pszName);
887 180 : sqlite3_stmt *hStmt = poReader->PrepareStatement(osSQL.c_str());
888 180 : if (poReader->ExecuteSQL(hStmt) != OGRERR_NONE)
889 0 : return false;
890 180 : const int nGeometries = sqlite3_column_int(hStmt, 0);
891 180 : sqlite3_finalize(hStmt);
892 :
893 180 : if (nGeometries < 1)
894 176 : return false;
895 :
896 12 : const bool bSkipInvalid = EQUAL(m_pszName, "OB") ||
897 8 : EQUAL(m_pszName, "OP") ||
898 4 : EQUAL(m_pszName, "OBBP");
899 :
900 : /* load geometry from DB */
901 : osSQL.Printf("SELECT %s,rowid,%s FROM %s ", GEOM_COLUMN, FID_COLUMN,
902 4 : m_pszName);
903 4 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
904 1 : osSQL += "WHERE PORADOVE_CISLO_BODU = 1 ";
905 4 : osSQL += "ORDER BY ";
906 4 : osSQL += FID_COLUMN;
907 4 : hStmt = poReader->PrepareStatement(osSQL.c_str());
908 :
909 4 : int rowId = 0;
910 4 : int nInvalid = 0;
911 4 : int nGeometriesCount = 0;
912 :
913 45 : while (poReader->ExecuteSQL(hStmt) == OGRERR_NONE)
914 : {
915 41 : rowId++; // =sqlite3_column_int(hStmt, 1);
916 41 : const GIntBig iFID = sqlite3_column_int64(hStmt, 2);
917 : VFKFeatureSQLite *poFeature =
918 41 : dynamic_cast<VFKFeatureSQLite *>(GetFeatureByIndex(rowId - 1));
919 41 : if (poFeature == nullptr || poFeature->GetFID() != iFID)
920 : {
921 0 : continue;
922 : }
923 :
924 : // read geometry from DB
925 41 : const int nBytes = sqlite3_column_bytes(hStmt, 0);
926 41 : OGRGeometry *poGeometry = nullptr;
927 41 : if (nBytes > 0 && OGRGeometryFactory::createFromWkb(
928 : sqlite3_column_blob(hStmt, 0), nullptr,
929 : &poGeometry, nBytes) == OGRERR_NONE)
930 : {
931 41 : nGeometriesCount++;
932 41 : if (!poFeature->SetGeometry(poGeometry))
933 : {
934 0 : nInvalid++;
935 : }
936 41 : delete poGeometry;
937 : }
938 : else
939 : {
940 0 : nInvalid++;
941 : }
942 : }
943 :
944 4 : CPLDebug("OGR-VFK", "%s: %d geometries loaded from DB", m_pszName,
945 : nGeometriesCount);
946 :
947 4 : if (nGeometriesCount != nGeometries)
948 : {
949 0 : CPLError(CE_Warning, CPLE_AppDefined,
950 : "%s: %d geometries loaded (should be %d)", m_pszName,
951 : nGeometriesCount, nGeometries);
952 : }
953 :
954 4 : if (nInvalid > 0 && !bSkipInvalid)
955 : {
956 0 : CPLError(CE_Warning, CPLE_AppDefined,
957 : "%s: %d features with invalid or empty geometry", m_pszName,
958 : nInvalid);
959 : }
960 :
961 4 : return true;
962 : }
963 :
964 : /*!
965 : \brief Update VFK_DB_TABLE table
966 :
967 : \param nGeometries number of geometries to update
968 : */
969 176 : void VFKDataBlockSQLite::UpdateVfkBlocks(int nGeometries)
970 : {
971 352 : CPLString osSQL;
972 :
973 176 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
974 :
975 : /* update number of features in VFK_DB_TABLE table */
976 176 : const int nFeatCount = (int)GetFeatureCount();
977 176 : if (nFeatCount > 0)
978 : {
979 : osSQL.Printf("UPDATE %s SET num_features = %d WHERE table_name = '%s'",
980 56 : VFK_DB_TABLE, nFeatCount, m_pszName);
981 56 : poReader->ExecuteSQL(osSQL.c_str());
982 : }
983 :
984 : /* update number of geometries in VFK_DB_TABLE table */
985 176 : if (nGeometries > 0)
986 : {
987 56 : CPLDebug("OGR-VFK",
988 : "VFKDataBlockSQLite::UpdateVfkBlocks(): name=%s -> "
989 : "%d geometries saved to internal DB",
990 : m_pszName, nGeometries);
991 :
992 : osSQL.Printf(
993 : "UPDATE %s SET num_geometries = %d WHERE table_name = '%s'",
994 56 : VFK_DB_TABLE, nGeometries, m_pszName);
995 56 : poReader->ExecuteSQL(osSQL.c_str());
996 : }
997 176 : }
998 :
999 : /*!
1000 : \brief Update feature id (see SBP)
1001 :
1002 : \param iFID feature id to set up
1003 : \param rowId list of rows to update
1004 : */
1005 196 : void VFKDataBlockSQLite::UpdateFID(GIntBig iFID, const std::vector<int> &rowId)
1006 : {
1007 392 : CPLString osSQL, osValue;
1008 196 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
1009 :
1010 : /* update number of geometries in VFK_DB_TABLE table */
1011 : osSQL.Printf("UPDATE %s SET %s = " CPL_FRMT_GIB " WHERE rowid IN (",
1012 196 : m_pszName, FID_COLUMN, iFID);
1013 602 : for (size_t i = 0; i < rowId.size(); i++)
1014 : {
1015 406 : if (i > 0)
1016 210 : osValue.Printf(",%d", rowId[i]);
1017 : else
1018 196 : osValue.Printf("%d", rowId[i]);
1019 406 : osSQL += osValue;
1020 : }
1021 196 : osSQL += ")";
1022 :
1023 196 : poReader->ExecuteSQL(osSQL.c_str());
1024 196 : }
1025 :
1026 : /*!
1027 : \brief Check is ring is closed
1028 :
1029 : \param poRing pointer to OGRLinearRing to check
1030 :
1031 : \return true if closed otherwise false
1032 : */
1033 0 : bool VFKDataBlockSQLite::IsRingClosed(const OGRLinearRing *poRing)
1034 : {
1035 0 : const int nPoints = poRing->getNumPoints();
1036 0 : if (nPoints < 3)
1037 0 : return false;
1038 :
1039 0 : if (poRing->getX(0) == poRing->getX(nPoints - 1) &&
1040 0 : poRing->getY(0) == poRing->getY(nPoints - 1))
1041 0 : return true;
1042 :
1043 0 : return false;
1044 : }
1045 :
1046 : /*!
1047 : \brief Get primary key
1048 :
1049 : \return property name or NULL
1050 : */
1051 168 : const char *VFKDataBlockSQLite::GetKey() const
1052 : {
1053 168 : if (GetPropertyCount() > 1)
1054 : {
1055 168 : const VFKPropertyDefn *poPropDefn = GetProperty(0);
1056 168 : const char *pszKey = poPropDefn->GetName();
1057 168 : if (EQUAL(pszKey, "ID"))
1058 168 : return pszKey;
1059 : }
1060 :
1061 0 : return nullptr;
1062 : }
1063 :
1064 : /*!
1065 : \brief Get geometry SQL type (for geometry_columns table)
1066 :
1067 : \return geometry_type as integer
1068 : */
1069 915 : int VFKDataBlockSQLite::GetGeometrySQLType() const
1070 : {
1071 915 : if (m_nGeometryType == wkbPolygon)
1072 28 : return 3;
1073 887 : else if (m_nGeometryType == wkbLineString)
1074 56 : return 2;
1075 831 : else if (m_nGeometryType == wkbPoint)
1076 84 : return 1;
1077 :
1078 747 : return 0; /* unknown geometry type */
1079 : }
1080 :
1081 : /*!
1082 : \brief Add geometry column into table if not exists
1083 :
1084 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1085 : */
1086 586 : OGRErr VFKDataBlockSQLite::AddGeometryColumn() const
1087 : {
1088 1172 : CPLString osSQL;
1089 :
1090 586 : VFKReaderSQLite *poReader = (VFKReaderSQLite *)m_poReader;
1091 :
1092 586 : osSQL.Printf("SELECT %s FROM %s LIMIT 0", GEOM_COLUMN, m_pszName);
1093 586 : if (poReader->ExecuteSQL(osSQL.c_str(), CE_None) == OGRERR_FAILURE)
1094 : {
1095 : /* query failed, we assume that geometry column not exists */
1096 0 : osSQL.Printf("ALTER TABLE %s ADD COLUMN %s blob", m_pszName,
1097 0 : GEOM_COLUMN);
1098 0 : return poReader->ExecuteSQL(osSQL.c_str());
1099 : }
1100 :
1101 586 : return OGRERR_NONE;
1102 : }
1103 :
1104 : /*!
1105 : \brief Load feature properties
1106 :
1107 : Used for sequential access, see OGRVFKLayer:GetNextFeature().
1108 :
1109 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1110 : */
1111 4 : OGRErr VFKDataBlockSQLite::LoadProperties()
1112 : {
1113 8 : CPLString osSQL;
1114 :
1115 4 : if (m_hStmt)
1116 0 : sqlite3_finalize(m_hStmt);
1117 :
1118 : osSQL.Printf("SELECT * FROM %s", // TODO: where
1119 4 : m_pszName);
1120 4 : if (EQUAL(m_pszName, "SBP") || EQUAL(m_pszName, "SBPG"))
1121 0 : osSQL += " WHERE PORADOVE_CISLO_BODU = 1";
1122 :
1123 4 : m_hStmt = ((VFKReaderSQLite *)m_poReader)->PrepareStatement(osSQL.c_str());
1124 :
1125 4 : if (m_hStmt == nullptr)
1126 0 : return OGRERR_FAILURE;
1127 :
1128 4 : return OGRERR_NONE;
1129 : }
1130 :
1131 : /*
1132 : \brief Clean feature properties for a next run
1133 :
1134 : \return OGRERR_NONE on success otherwise OGRERR_FAILURE
1135 : */
1136 979 : OGRErr VFKDataBlockSQLite::CleanProperties()
1137 : {
1138 979 : if (m_hStmt)
1139 : {
1140 4 : if (sqlite3_finalize(m_hStmt) != SQLITE_OK)
1141 : {
1142 0 : m_hStmt = nullptr;
1143 0 : return OGRERR_FAILURE;
1144 : }
1145 4 : m_hStmt = nullptr;
1146 : }
1147 :
1148 979 : return OGRERR_NONE;
1149 : }
|