1 : /******************************************************************************
2 : * $Id: ogrshapedatasource.cpp 24645 2012-07-01 20:47:05Z rouault $
3 : *
4 : * Project: OpenGIS Simple Features Reference Implementation
5 : * Purpose: Implements OGRShapeDataSource class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 1999, Les Technologies SoftMap Inc.
10 : *
11 : * Permission is hereby granted, free of charge, to any person obtaining a
12 : * copy of this software and associated documentation files (the "Software"),
13 : * to deal in the Software without restriction, including without limitation
14 : * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 : * and/or sell copies of the Software, and to permit persons to whom the
16 : * Software is furnished to do so, subject to the following conditions:
17 : *
18 : * The above copyright notice and this permission notice shall be included
19 : * in all copies or substantial portions of the Software.
20 : *
21 : * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 : * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 : * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 : * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 : * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 : * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 : * DEALINGS IN THE SOFTWARE.
28 : ****************************************************************************/
29 :
30 : #include "ogrshape.h"
31 : #include "cpl_conv.h"
32 : #include "cpl_string.h"
33 : #include <set>
34 :
35 : //#define IMMEDIATE_OPENING 1
36 :
37 : CPL_CVSID("$Id: ogrshapedatasource.cpp 24645 2012-07-01 20:47:05Z rouault $");
38 :
39 : /************************************************************************/
40 : /* OGRShapeDataSource() */
41 : /************************************************************************/
42 :
43 1663 : OGRShapeDataSource::OGRShapeDataSource()
44 :
45 : {
46 1663 : pszName = NULL;
47 1663 : papoLayers = NULL;
48 1663 : nLayers = 0;
49 1663 : bSingleFileDataSource = FALSE;
50 1663 : poPool = new OGRLayerPool();
51 1663 : }
52 :
53 : /************************************************************************/
54 : /* ~OGRShapeDataSource() */
55 : /************************************************************************/
56 :
57 1663 : OGRShapeDataSource::~OGRShapeDataSource()
58 :
59 : {
60 1663 : CPLFree( pszName );
61 :
62 5063 : for( int i = 0; i < nLayers; i++ )
63 : {
64 3400 : CPLAssert( NULL != papoLayers[i] );
65 :
66 3400 : delete papoLayers[i];
67 : }
68 :
69 1663 : delete poPool;
70 :
71 1663 : CPLFree( papoLayers );
72 1663 : }
73 :
74 : /************************************************************************/
75 : /* Open() */
76 : /************************************************************************/
77 :
78 1663 : int OGRShapeDataSource::Open( const char * pszNewName, int bUpdate,
79 : int bTestOpen, int bForceSingleFileDataSource )
80 :
81 : {
82 : VSIStatBufL stat;
83 :
84 1663 : CPLAssert( nLayers == 0 );
85 :
86 1663 : pszName = CPLStrdup( pszNewName );
87 :
88 1663 : bDSUpdate = bUpdate;
89 :
90 1663 : bSingleFileDataSource = bForceSingleFileDataSource;
91 :
92 : /* -------------------------------------------------------------------- */
93 : /* If bSingleFileDataSource is TRUE we don't try to do anything else. */
94 : /* This is only utilized when the OGRShapeDriver::Create() */
95 : /* method wants to create a stub OGRShapeDataSource for a */
96 : /* single shapefile. The driver will take care of creating the */
97 : /* file by calling CreateLayer(). */
98 : /* -------------------------------------------------------------------- */
99 1663 : if( bSingleFileDataSource )
100 108 : return TRUE;
101 :
102 : /* -------------------------------------------------------------------- */
103 : /* Is the given path a directory or a regular file? */
104 : /* -------------------------------------------------------------------- */
105 1555 : if( VSIStatExL( pszNewName, &stat, VSI_STAT_EXISTS_FLAG | VSI_STAT_NATURE_FLAG ) != 0
106 : || (!VSI_ISDIR(stat.st_mode) && !VSI_ISREG(stat.st_mode)) )
107 : {
108 205 : if( !bTestOpen )
109 : CPLError( CE_Failure, CPLE_AppDefined,
110 : "%s is neither a file or directory, Shape access failed.\n",
111 0 : pszNewName );
112 :
113 205 : return FALSE;
114 : }
115 :
116 : /* -------------------------------------------------------------------- */
117 : /* Build a list of filenames we figure are Shape files. */
118 : /* -------------------------------------------------------------------- */
119 1350 : if( VSI_ISREG(stat.st_mode) )
120 : {
121 1176 : if( !OpenFile( pszNewName, bUpdate, bTestOpen ) )
122 : {
123 713 : if( !bTestOpen )
124 : CPLError( CE_Failure, CPLE_OpenFailed,
125 : "Failed to open shapefile %s.\n"
126 : "It may be corrupt or read-only file accessed in update mode.\n",
127 0 : pszNewName );
128 :
129 713 : return FALSE;
130 : }
131 :
132 463 : bSingleFileDataSource = TRUE;
133 :
134 463 : return TRUE;
135 : }
136 : else
137 : {
138 174 : char **papszCandidates = CPLReadDir( pszNewName );
139 174 : int iCan, nCandidateCount = CSLCount( papszCandidates );
140 174 : int bMightBeOldCoverage = FALSE;
141 174 : std::set<CPLString> osLayerNameSet;
142 :
143 17436 : for( iCan = 0; iCan < nCandidateCount; iCan++ )
144 : {
145 : char *pszFilename;
146 17262 : const char *pszCandidate = papszCandidates[iCan];
147 :
148 17262 : if( EQUAL(pszCandidate,"ARC") )
149 0 : bMightBeOldCoverage = TRUE;
150 :
151 17262 : if( strlen(pszCandidate) < 4
152 : || !EQUAL(pszCandidate+strlen(pszCandidate)-4,".shp") )
153 13553 : continue;
154 :
155 : pszFilename =
156 3709 : CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, NULL));
157 :
158 3709 : osLayerNameSet.insert(CPLGetBasename(pszCandidate));
159 : #ifdef IMMEDIATE_OPENING
160 : if( !OpenFile( pszFilename, bUpdate, bTestOpen )
161 : && !bTestOpen )
162 : {
163 : CPLError( CE_Failure, CPLE_OpenFailed,
164 : "Failed to open shapefile %s.\n"
165 : "It may be corrupt or read-only file accessed in update mode.\n",
166 : pszFilename );
167 : CPLFree( pszFilename );
168 : CSLDestroy( papszCandidates );
169 : return FALSE;
170 : }
171 : #else
172 3709 : oVectorLayerName.push_back(pszFilename);
173 : #endif
174 3709 : CPLFree( pszFilename );
175 : }
176 :
177 : // Try and .dbf files without apparent associated shapefiles.
178 17436 : for( iCan = 0; iCan < nCandidateCount; iCan++ )
179 : {
180 : char *pszFilename;
181 17262 : const char *pszCandidate = papszCandidates[iCan];
182 : const char *pszLayerName;
183 :
184 : // We don't consume .dbf files in a directory that looks like
185 : // an old style Arc/Info (for PC?) that unless we found at least
186 : // some shapefiles. See Bug 493.
187 17262 : if( bMightBeOldCoverage && osLayerNameSet.size() == 0 )
188 0 : continue;
189 :
190 17262 : if( strlen(pszCandidate) < 4
191 : || !EQUAL(pszCandidate+strlen(pszCandidate)-4,".dbf") )
192 13613 : continue;
193 :
194 3649 : pszLayerName = CPLGetBasename(pszCandidate);
195 3649 : if (osLayerNameSet.find(pszLayerName) != osLayerNameSet.end())
196 3529 : continue;
197 :
198 : // We don't want to access .dbf files with an associated .tab
199 : // file, or it will never get recognised as a mapinfo dataset.
200 120 : int iCan2, bFoundTAB = FALSE;
201 33000 : for( iCan2 = 0; iCan2 < nCandidateCount; iCan2++ )
202 : {
203 32880 : const char *pszCandidate2 = papszCandidates[iCan2];
204 :
205 32880 : if( EQUALN(pszCandidate2,pszLayerName,strlen(pszLayerName))
206 : && EQUAL(pszCandidate2 + strlen(pszLayerName), ".tab") )
207 0 : bFoundTAB = TRUE;
208 : }
209 :
210 120 : if( bFoundTAB )
211 0 : continue;
212 :
213 : pszFilename =
214 120 : CPLStrdup(CPLFormFilename(pszNewName, pszCandidate, NULL));
215 :
216 120 : osLayerNameSet.insert(CPLGetBasename(pszCandidate));
217 :
218 : #ifdef IMMEDIATE_OPENING
219 : if( !OpenFile( pszFilename, bUpdate, bTestOpen )
220 : && !bTestOpen )
221 : {
222 : CPLError( CE_Failure, CPLE_OpenFailed,
223 : "Failed to open dbf file %s.\n"
224 : "It may be corrupt or read-only file accessed in update mode.\n",
225 : pszFilename );
226 : CPLFree( pszFilename );
227 : CSLDestroy( papszCandidates );
228 : return FALSE;
229 : }
230 : #else
231 120 : oVectorLayerName.push_back(pszFilename);
232 : #endif
233 120 : CPLFree( pszFilename );
234 : }
235 :
236 174 : CSLDestroy( papszCandidates );
237 :
238 : #ifdef IMMEDIATE_OPENING
239 : int nDirLayers = nLayers;
240 : #else
241 174 : int nDirLayers = oVectorLayerName.size();
242 : #endif
243 :
244 174 : CPLErrorReset();
245 :
246 174 : return nDirLayers > 0 || !bTestOpen;
247 : }
248 : }
249 :
250 : /************************************************************************/
251 : /* OpenFile() */
252 : /************************************************************************/
253 :
254 3404 : int OGRShapeDataSource::OpenFile( const char *pszNewName, int bUpdate,
255 : int bTestOpen )
256 :
257 : {
258 : SHPHandle hSHP;
259 : DBFHandle hDBF;
260 3404 : const char *pszExtension = CPLGetExtension( pszNewName );
261 :
262 : (void) bTestOpen;
263 :
264 3404 : if( !EQUAL(pszExtension,"shp") && !EQUAL(pszExtension,"shx")
265 : && !EQUAL(pszExtension,"dbf") )
266 713 : return FALSE;
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* SHPOpen() should include better (CPL based) error reporting, */
270 : /* and we should be trying to distinquish at this point whether */
271 : /* failure is a result of trying to open a non-shapefile, or */
272 : /* whether it was a shapefile and we want to report the error */
273 : /* up. */
274 : /* */
275 : /* Care is taken to suppress the error and only reissue it if */
276 : /* we think it is appropriate. */
277 : /* -------------------------------------------------------------------- */
278 2691 : CPLPushErrorHandler( CPLQuietErrorHandler );
279 2691 : if( bUpdate )
280 1710 : hSHP = SHPOpen( pszNewName, "r+" );
281 : else
282 981 : hSHP = SHPOpen( pszNewName, "r" );
283 2691 : CPLPopErrorHandler();
284 :
285 2691 : if( hSHP == NULL
286 : && (!EQUAL(CPLGetExtension(pszNewName),"dbf")
287 : || strstr(CPLGetLastErrorMsg(),".shp") == NULL) )
288 : {
289 1 : CPLString osMsg = CPLGetLastErrorMsg();
290 :
291 1 : CPLError( CE_Failure, CPLE_OpenFailed, "%s", osMsg.c_str() );
292 :
293 1 : return FALSE;
294 : }
295 2690 : CPLErrorReset();
296 :
297 : /* -------------------------------------------------------------------- */
298 : /* Open the .dbf file, if it exists. To open a dbf file, the */
299 : /* filename has to either refer to a successfully opened shp */
300 : /* file or has to refer to the actual .dbf file. */
301 : /* -------------------------------------------------------------------- */
302 2690 : if( hSHP != NULL || EQUAL(CPLGetExtension(pszNewName),"dbf") )
303 : {
304 2690 : if( bUpdate )
305 : {
306 1710 : hDBF = DBFOpen( pszNewName, "r+" );
307 1710 : if( hSHP != NULL && hDBF == NULL )
308 : {
309 : VSIStatBufL sStat;
310 3 : const char* pszDBFName = CPLResetExtension(pszNewName, "dbf");
311 3 : VSILFILE* fp = NULL;
312 3 : if( VSIStatExL( pszDBFName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
313 : {
314 0 : fp = VSIFOpenL(pszDBFName, "r+");
315 0 : if (fp == NULL)
316 : {
317 : CPLError( CE_Failure, CPLE_OpenFailed,
318 : "%s exists, but cannot be opened in update mode",
319 0 : pszDBFName );
320 0 : return FALSE;
321 : }
322 : }
323 : else
324 : {
325 3 : pszDBFName = CPLResetExtension(pszNewName, "DBF");
326 3 : if( VSIStatExL( pszDBFName, &sStat, VSI_STAT_EXISTS_FLAG) == 0 )
327 : {
328 0 : fp = VSIFOpenL(pszDBFName, "r+");
329 0 : if (fp == NULL)
330 : {
331 : CPLError( CE_Failure, CPLE_OpenFailed,
332 : "%s exists, but cannot be opened in update mode",
333 0 : pszDBFName );
334 0 : return FALSE;
335 : }
336 : }
337 : }
338 3 : if (fp != NULL)
339 0 : VSIFCloseL(fp);
340 : }
341 : }
342 : else
343 980 : hDBF = DBFOpen( pszNewName, "r" );
344 : }
345 : else
346 0 : hDBF = NULL;
347 :
348 2690 : if( hDBF == NULL && hSHP == NULL )
349 0 : return FALSE;
350 :
351 : /* -------------------------------------------------------------------- */
352 : /* Create the layer object. */
353 : /* -------------------------------------------------------------------- */
354 : OGRShapeLayer *poLayer;
355 :
356 : poLayer = new OGRShapeLayer( this, pszNewName, hSHP, hDBF, NULL, FALSE, bUpdate,
357 2690 : wkbNone );
358 :
359 : /* -------------------------------------------------------------------- */
360 : /* Add layer to data source layer list. */
361 : /* -------------------------------------------------------------------- */
362 2690 : AddLayer(poLayer);
363 :
364 2690 : return TRUE;
365 : }
366 :
367 :
368 : /************************************************************************/
369 : /* AddLayer() */
370 : /************************************************************************/
371 :
372 3908 : void OGRShapeDataSource::AddLayer(OGRShapeLayer* poLayer)
373 : {
374 : papoLayers = (OGRShapeLayer **)
375 3908 : CPLRealloc( papoLayers, sizeof(OGRShapeLayer *) * (nLayers+1) );
376 3908 : papoLayers[nLayers++] = poLayer;
377 :
378 : /* If we reach the limit, then register all the already opened layers */
379 : /* Technically this code would not be necessary if there was not the */
380 : /* following initial test in SetLastUsedLayer() : */
381 : /* if (nLayers < MAX_SIMULTANEOUSLY_OPENED_LAYERS) */
382 : /* return; */
383 3908 : if (nLayers == poPool->GetMaxSimultaneouslyOpened() && poPool->GetSize() == 0)
384 : {
385 505 : for(int i=0;i<nLayers;i++)
386 500 : poPool->SetLastUsedLayer(papoLayers[i]);
387 : }
388 3908 : }
389 :
390 : /************************************************************************/
391 : /* CreateLayer() */
392 : /************************************************************************/
393 :
394 : OGRLayer *
395 1222 : OGRShapeDataSource::CreateLayer( const char * pszLayerName,
396 : OGRSpatialReference *poSRS,
397 : OGRwkbGeometryType eType,
398 : char ** papszOptions )
399 :
400 : {
401 : SHPHandle hSHP;
402 : DBFHandle hDBF;
403 : int nShapeType;
404 :
405 : /* To ensure that existing layers are created */
406 1222 : GetLayerCount();
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Check that the layer doesn't already exist. */
410 : /* -------------------------------------------------------------------- */
411 1222 : if (GetLayerByName(pszLayerName) != NULL)
412 : {
413 : CPLError( CE_Failure, CPLE_AppDefined, "Layer '%s' already exists",
414 2 : pszLayerName);
415 2 : return NULL;
416 : }
417 :
418 : /* -------------------------------------------------------------------- */
419 : /* Verify we are in update mode. */
420 : /* -------------------------------------------------------------------- */
421 1220 : if( !bDSUpdate )
422 : {
423 : CPLError( CE_Failure, CPLE_NoWriteAccess,
424 : "Data source %s opened read-only.\n"
425 : "New layer %s cannot be created.\n",
426 0 : pszName, pszLayerName );
427 :
428 0 : return NULL;
429 : }
430 :
431 : /* -------------------------------------------------------------------- */
432 : /* Figure out what type of layer we need. */
433 : /* -------------------------------------------------------------------- */
434 2326 : if( eType == wkbUnknown || eType == wkbLineString )
435 1106 : nShapeType = SHPT_ARC;
436 114 : else if( eType == wkbPoint )
437 7 : nShapeType = SHPT_POINT;
438 107 : else if( eType == wkbPolygon )
439 71 : nShapeType = SHPT_POLYGON;
440 36 : else if( eType == wkbMultiPoint )
441 2 : nShapeType = SHPT_MULTIPOINT;
442 34 : else if( eType == wkbPoint25D )
443 1 : nShapeType = SHPT_POINTZ;
444 33 : else if( eType == wkbLineString25D )
445 4 : nShapeType = SHPT_ARCZ;
446 29 : else if( eType == wkbMultiLineString )
447 5 : nShapeType = SHPT_ARC;
448 24 : else if( eType == wkbMultiLineString25D )
449 1 : nShapeType = SHPT_ARCZ;
450 23 : else if( eType == wkbPolygon25D )
451 7 : nShapeType = SHPT_POLYGONZ;
452 16 : else if( eType == wkbMultiPolygon )
453 9 : nShapeType = SHPT_POLYGON;
454 7 : else if( eType == wkbMultiPolygon25D )
455 1 : nShapeType = SHPT_POLYGONZ;
456 6 : else if( eType == wkbMultiPoint25D )
457 1 : nShapeType = SHPT_MULTIPOINTZ;
458 5 : else if( eType == wkbNone )
459 5 : nShapeType = SHPT_NULL;
460 : else
461 0 : nShapeType = -1;
462 :
463 : /* -------------------------------------------------------------------- */
464 : /* Has the application overridden this with a special creation */
465 : /* option? */
466 : /* -------------------------------------------------------------------- */
467 1220 : const char *pszOverride = CSLFetchNameValue( papszOptions, "SHPT" );
468 :
469 1220 : if( pszOverride == NULL )
470 : /* ignore */;
471 2 : else if( EQUAL(pszOverride,"POINT") )
472 : {
473 0 : nShapeType = SHPT_POINT;
474 0 : eType = wkbPoint;
475 : }
476 2 : else if( EQUAL(pszOverride,"ARC") )
477 : {
478 0 : nShapeType = SHPT_ARC;
479 0 : eType = wkbLineString;
480 : }
481 2 : else if( EQUAL(pszOverride,"POLYGON") )
482 : {
483 0 : nShapeType = SHPT_POLYGON;
484 0 : eType = wkbPolygon;
485 : }
486 2 : else if( EQUAL(pszOverride,"MULTIPOINT") )
487 : {
488 0 : nShapeType = SHPT_MULTIPOINT;
489 0 : eType = wkbMultiPoint;
490 : }
491 2 : else if( EQUAL(pszOverride,"POINTZ") )
492 : {
493 0 : nShapeType = SHPT_POINTZ;
494 0 : eType = wkbPoint25D;
495 : }
496 2 : else if( EQUAL(pszOverride,"ARCZ") )
497 : {
498 0 : nShapeType = SHPT_ARCZ;
499 0 : eType = wkbLineString25D;
500 : }
501 2 : else if( EQUAL(pszOverride,"POLYGONZ") )
502 : {
503 2 : nShapeType = SHPT_POLYGONZ;
504 2 : eType = wkbPolygon25D;
505 : }
506 0 : else if( EQUAL(pszOverride,"MULTIPOINTZ") )
507 : {
508 0 : nShapeType = SHPT_MULTIPOINTZ;
509 0 : eType = wkbMultiPoint25D;
510 : }
511 0 : else if( EQUAL(pszOverride,"NONE") || EQUAL(pszOverride,"NULL") )
512 : {
513 0 : nShapeType = SHPT_NULL;
514 0 : eType = wkbNone;
515 : }
516 : else
517 : {
518 : CPLError( CE_Failure, CPLE_NotSupported,
519 : "Unknown SHPT value of `%s' passed to Shapefile layer\n"
520 : "creation. Creation aborted.\n",
521 0 : pszOverride );
522 :
523 0 : return NULL;
524 : }
525 :
526 1220 : if( nShapeType == -1 )
527 : {
528 : CPLError( CE_Failure, CPLE_NotSupported,
529 : "Geometry type of `%s' not supported in shapefiles.\n"
530 : "Type can be overridden with a layer creation option\n"
531 : "of SHPT=POINT/ARC/POLYGON/MULTIPOINT/POINTZ/ARCZ/POLYGONZ/MULTIPOINTZ.\n",
532 0 : OGRGeometryTypeToName(eType) );
533 0 : return NULL;
534 : }
535 :
536 : /* -------------------------------------------------------------------- */
537 : /* What filename do we use, excluding the extension? */
538 : /* -------------------------------------------------------------------- */
539 : char *pszFilenameWithoutExt;
540 :
541 1330 : if( bSingleFileDataSource && nLayers == 0 )
542 : {
543 110 : char *pszPath = CPLStrdup(CPLGetPath(pszName));
544 110 : char *pszFBasename = CPLStrdup(CPLGetBasename(pszName));
545 :
546 110 : pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszPath, pszFBasename, NULL));
547 :
548 110 : CPLFree( pszFBasename );
549 110 : CPLFree( pszPath );
550 : }
551 1110 : else if( bSingleFileDataSource )
552 : {
553 : /* This is a very weird use case : the user creates/open a datasource */
554 : /* made of a single shapefile 'foo.shp' and wants to add a new layer */
555 : /* to it, 'bar'. So we create a new shapefile 'bar.shp' in the same */
556 : /* directory as 'foo.shp' */
557 : /* So technically, we will not be any longer a single file */
558 : /* datasource ... Ahem ahem */
559 1 : char *pszPath = CPLStrdup(CPLGetPath(pszName));
560 1 : pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszPath,pszLayerName,NULL));
561 1 : CPLFree( pszPath );
562 : }
563 : else
564 1109 : pszFilenameWithoutExt = CPLStrdup(CPLFormFilename(pszName,pszLayerName,NULL));
565 :
566 : /* -------------------------------------------------------------------- */
567 : /* Create the shapefile. */
568 : /* -------------------------------------------------------------------- */
569 : char *pszFilename;
570 :
571 1220 : if( nShapeType != SHPT_NULL )
572 : {
573 1215 : pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "shp" ));
574 :
575 1215 : hSHP = SHPCreate( pszFilename, nShapeType );
576 :
577 1215 : if( hSHP == NULL )
578 : {
579 : CPLError( CE_Failure, CPLE_OpenFailed,
580 : "Failed to open Shapefile `%s'.\n",
581 1 : pszFilename );
582 1 : CPLFree( pszFilename );
583 1 : CPLFree( pszFilenameWithoutExt );
584 1 : return NULL;
585 : }
586 1214 : CPLFree( pszFilename );
587 : }
588 : else
589 5 : hSHP = NULL;
590 :
591 : /* -------------------------------------------------------------------- */
592 : /* Has a specific LDID been specified by the caller? */
593 : /* -------------------------------------------------------------------- */
594 1219 : const char *pszLDID = CSLFetchNameValue( papszOptions, "ENCODING" );
595 :
596 : /* -------------------------------------------------------------------- */
597 : /* Create a DBF file. */
598 : /* -------------------------------------------------------------------- */
599 1219 : pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "dbf" ));
600 :
601 1219 : if( pszLDID != NULL )
602 0 : hDBF = DBFCreateEx( pszFilename, pszLDID );
603 : else
604 1219 : hDBF = DBFCreate( pszFilename );
605 :
606 1219 : if( hDBF == NULL )
607 : {
608 : CPLError( CE_Failure, CPLE_OpenFailed,
609 : "Failed to open Shape DBF file `%s'.\n",
610 1 : pszFilename );
611 1 : CPLFree( pszFilename );
612 1 : CPLFree( pszFilenameWithoutExt );
613 1 : SHPClose(hSHP);
614 1 : return NULL;
615 : }
616 :
617 1218 : CPLFree( pszFilename );
618 :
619 : /* -------------------------------------------------------------------- */
620 : /* Create the .prj file, if required. */
621 : /* -------------------------------------------------------------------- */
622 1218 : if( poSRS != NULL )
623 : {
624 102 : char *pszWKT = NULL;
625 102 : CPLString osPrjFile = CPLFormFilename( NULL, pszFilenameWithoutExt, "prj");
626 : VSILFILE *fp;
627 :
628 : /* the shape layer needs it's own copy */
629 102 : poSRS = poSRS->Clone();
630 102 : poSRS->morphToESRI();
631 :
632 102 : if( poSRS->exportToWkt( &pszWKT ) == OGRERR_NONE
633 : && (fp = VSIFOpenL( osPrjFile, "wt" )) != NULL )
634 : {
635 102 : VSIFWriteL( pszWKT, strlen(pszWKT), 1, fp );
636 102 : VSIFCloseL( fp );
637 : }
638 :
639 102 : CPLFree( pszWKT );
640 :
641 102 : poSRS->morphFromESRI();
642 : }
643 :
644 : /* -------------------------------------------------------------------- */
645 : /* Create the layer object. */
646 : /* -------------------------------------------------------------------- */
647 : OGRShapeLayer *poLayer;
648 :
649 : /* OGRShapeLayer constructor expects a filename with an extension (that could be */
650 : /* random actually), otherwise this is going to cause problems with layer */
651 : /* names that have a dot (not speaking about the one before the shp) */
652 1218 : pszFilename = CPLStrdup(CPLFormFilename( NULL, pszFilenameWithoutExt, "shp" ));
653 :
654 : poLayer = new OGRShapeLayer( this, pszFilename, hSHP, hDBF, poSRS, TRUE, TRUE,
655 1218 : eType );
656 :
657 1218 : CPLFree( pszFilenameWithoutExt );
658 1218 : CPLFree( pszFilename );
659 :
660 1218 : poLayer->SetResizeAtClose( CSLFetchBoolean( papszOptions, "RESIZE", FALSE ) );
661 :
662 : /* -------------------------------------------------------------------- */
663 : /* Add layer to data source layer list. */
664 : /* -------------------------------------------------------------------- */
665 1218 : AddLayer(poLayer);
666 :
667 1218 : return poLayer;
668 : }
669 :
670 : /************************************************************************/
671 : /* TestCapability() */
672 : /************************************************************************/
673 :
674 94 : int OGRShapeDataSource::TestCapability( const char * pszCap )
675 :
676 : {
677 94 : if( EQUAL(pszCap,ODsCCreateLayer) )
678 92 : return bDSUpdate;
679 2 : else if( EQUAL(pszCap,ODsCDeleteLayer) )
680 0 : return bDSUpdate;
681 : else
682 2 : return FALSE;
683 : }
684 :
685 : /************************************************************************/
686 : /* GetLayerCount() */
687 : /************************************************************************/
688 :
689 1091267 : int OGRShapeDataSource::GetLayerCount()
690 :
691 : {
692 : #ifndef IMMEDIATE_OPENING
693 1091267 : if (oVectorLayerName.size() != 0)
694 : {
695 1746 : for(size_t i = 0; i < oVectorLayerName.size(); i++)
696 : {
697 1668 : const char* pszFilename = oVectorLayerName[i].c_str();
698 1668 : const char* pszLayerName = CPLGetBasename(pszFilename);
699 :
700 : int j;
701 256990 : for(j=0;j<nLayers;j++)
702 : {
703 255878 : if (strcmp(papoLayers[j]->GetName(), pszLayerName) == 0)
704 556 : break;
705 : }
706 1668 : if (j < nLayers)
707 556 : continue;
708 :
709 1112 : if( !OpenFile( pszFilename, bDSUpdate, TRUE ) )
710 : {
711 : CPLError( CE_Failure, CPLE_OpenFailed,
712 : "Failed to open file %s.\n"
713 : "It may be corrupt or read-only file accessed in update mode.\n",
714 0 : pszFilename );
715 : }
716 : }
717 78 : oVectorLayerName.resize(0);
718 : }
719 : #endif
720 :
721 1091267 : return nLayers;
722 : }
723 :
724 : /************************************************************************/
725 : /* GetLayer() */
726 : /************************************************************************/
727 :
728 543527 : OGRLayer *OGRShapeDataSource::GetLayer( int iLayer )
729 :
730 : {
731 : /* To ensure that existing layers are created */
732 543527 : GetLayerCount();
733 :
734 543527 : if( iLayer < 0 || iLayer >= nLayers )
735 4 : return NULL;
736 : else
737 543523 : return papoLayers[iLayer];
738 : }
739 :
740 : /************************************************************************/
741 : /* GetLayerByName() */
742 : /************************************************************************/
743 :
744 3286 : OGRLayer *OGRShapeDataSource::GetLayerByName(const char * pszLayerNameIn)
745 : {
746 : #ifndef IMMEDIATE_OPENING
747 3286 : if (oVectorLayerName.size() != 0)
748 : {
749 : int j;
750 252752 : for(j=0;j<nLayers;j++)
751 : {
752 251632 : if (strcmp(papoLayers[j]->GetName(), pszLayerNameIn) == 0)
753 : {
754 583 : return papoLayers[j];
755 : }
756 : }
757 :
758 : size_t i;
759 1131 : for(j = 0; j < 2; j++)
760 : {
761 252046 : for(i = 0; i < oVectorLayerName.size(); i++)
762 : {
763 252035 : const char* pszFilename = oVectorLayerName[i].c_str();
764 252035 : const char* pszLayerName = CPLGetBasename(pszFilename);
765 :
766 252035 : if (j == 0)
767 : {
768 251970 : if (strcmp(pszLayerName, pszLayerNameIn) != 0)
769 250857 : continue;
770 : }
771 : else
772 : {
773 65 : if ( !EQUAL(pszLayerName, pszLayerNameIn) )
774 62 : continue;
775 : }
776 :
777 1116 : if( !OpenFile( pszFilename, bDSUpdate, TRUE ) )
778 : {
779 : CPLError( CE_Failure, CPLE_OpenFailed,
780 : "Failed to open file %s.\n"
781 : "It may be corrupt or read-only file accessed in update mode.\n",
782 1 : pszFilename );
783 1 : return NULL;
784 : }
785 : else
786 : {
787 1115 : return papoLayers[nLayers - 1];
788 : }
789 : }
790 : }
791 :
792 4 : return NULL;
793 : }
794 : #endif
795 :
796 1583 : return OGRDataSource::GetLayerByName(pszLayerNameIn);
797 : }
798 :
799 : /************************************************************************/
800 : /* ExecuteSQL() */
801 : /* */
802 : /* We override this to provide special handling of CREATE */
803 : /* SPATIAL INDEX commands. Support forms are: */
804 : /* */
805 : /* CREATE SPATIAL INDEX ON layer_name [DEPTH n] */
806 : /* DROP SPATIAL INDEX ON layer_name */
807 : /* REPACK layer_name */
808 : /* RECOMPUTE EXTENT ON layer_name */
809 : /************************************************************************/
810 :
811 743 : OGRLayer * OGRShapeDataSource::ExecuteSQL( const char *pszStatement,
812 : OGRGeometry *poSpatialFilter,
813 : const char *pszDialect )
814 :
815 : {
816 : /* ==================================================================== */
817 : /* Handle command to drop a spatial index. */
818 : /* ==================================================================== */
819 743 : if( EQUALN(pszStatement, "REPACK ", 7) )
820 : {
821 : OGRShapeLayer *poLayer = (OGRShapeLayer *)
822 6 : GetLayerByName( pszStatement + 7 );
823 :
824 6 : if( poLayer != NULL )
825 6 : poLayer->Repack();
826 : else
827 : {
828 : CPLError( CE_Failure, CPLE_AppDefined,
829 : "No such layer as '%s' in REPACK.",
830 0 : pszStatement + 7 );
831 : }
832 6 : return NULL;
833 : }
834 :
835 : /* ==================================================================== */
836 : /* Handle command to shrink columns to their minimum size. */
837 : /* ==================================================================== */
838 737 : if( EQUALN(pszStatement, "RESIZE ", 7) )
839 : {
840 : OGRShapeLayer *poLayer = (OGRShapeLayer *)
841 1 : GetLayerByName( pszStatement + 7 );
842 :
843 1 : if( poLayer != NULL )
844 1 : poLayer->ResizeDBF();
845 : else
846 : {
847 : CPLError( CE_Failure, CPLE_AppDefined,
848 : "No such layer as '%s' in RESIZE.",
849 0 : pszStatement + 7 );
850 : }
851 1 : return NULL;
852 : }
853 :
854 : /* ==================================================================== */
855 : /* Handle command to recompute extent */
856 : /* ==================================================================== */
857 736 : if( EQUALN(pszStatement, "RECOMPUTE EXTENT ON ", 20) )
858 : {
859 : OGRShapeLayer *poLayer = (OGRShapeLayer *)
860 3 : GetLayerByName( pszStatement + 20 );
861 :
862 3 : if( poLayer != NULL )
863 2 : poLayer->RecomputeExtent();
864 : else
865 : {
866 : CPLError( CE_Failure, CPLE_AppDefined,
867 : "No such layer as '%s' in RECOMPUTE EXTENT.",
868 1 : pszStatement + 20 );
869 : }
870 3 : return NULL;
871 : }
872 :
873 : /* ==================================================================== */
874 : /* Handle command to drop a spatial index. */
875 : /* ==================================================================== */
876 733 : if( EQUALN(pszStatement, "DROP SPATIAL INDEX ON ", 22) )
877 : {
878 : OGRShapeLayer *poLayer = (OGRShapeLayer *)
879 2 : GetLayerByName( pszStatement + 22 );
880 :
881 2 : if( poLayer != NULL )
882 2 : poLayer->DropSpatialIndex();
883 : else
884 : {
885 : CPLError( CE_Failure, CPLE_AppDefined,
886 : "No such layer as '%s' in DROP SPATIAL INDEX.",
887 0 : pszStatement + 22 );
888 : }
889 2 : return NULL;
890 : }
891 :
892 : /* ==================================================================== */
893 : /* Handle all comands except spatial index creation generically. */
894 : /* ==================================================================== */
895 731 : if( !EQUALN(pszStatement,"CREATE SPATIAL INDEX ON ",24) )
896 : {
897 717 : char **papszTokens = CSLTokenizeString( pszStatement );
898 1159 : if( CSLCount(papszTokens) >=4
899 422 : && (EQUAL(papszTokens[0],"CREATE") || EQUAL(papszTokens[0],"DROP"))
900 10 : && EQUAL(papszTokens[1],"INDEX")
901 10 : && EQUAL(papszTokens[2],"ON") )
902 : {
903 10 : OGRShapeLayer *poLayer = (OGRShapeLayer *) GetLayerByName(papszTokens[3]);
904 10 : if (poLayer != NULL)
905 10 : poLayer->InitializeIndexSupport( poLayer->GetFullName() );
906 : }
907 717 : CSLDestroy( papszTokens );
908 :
909 : return OGRDataSource::ExecuteSQL( pszStatement, poSpatialFilter,
910 717 : pszDialect );
911 : }
912 :
913 : /* -------------------------------------------------------------------- */
914 : /* Parse into keywords. */
915 : /* -------------------------------------------------------------------- */
916 14 : char **papszTokens = CSLTokenizeString( pszStatement );
917 :
918 70 : if( CSLCount(papszTokens) < 5
919 14 : || !EQUAL(papszTokens[0],"CREATE")
920 14 : || !EQUAL(papszTokens[1],"SPATIAL")
921 14 : || !EQUAL(papszTokens[2],"INDEX")
922 14 : || !EQUAL(papszTokens[3],"ON")
923 : || CSLCount(papszTokens) > 7
924 0 : || (CSLCount(papszTokens) == 7 && !EQUAL(papszTokens[5],"DEPTH")) )
925 : {
926 0 : CSLDestroy( papszTokens );
927 : CPLError( CE_Failure, CPLE_AppDefined,
928 : "Syntax error in CREATE SPATIAL INDEX command.\n"
929 : "Was '%s'\n"
930 : "Should be of form 'CREATE SPATIAL INDEX ON <table> [DEPTH <n>]'",
931 0 : pszStatement );
932 0 : return NULL;
933 : }
934 :
935 : /* -------------------------------------------------------------------- */
936 : /* Get depth if provided. */
937 : /* -------------------------------------------------------------------- */
938 14 : int nDepth = 0;
939 14 : if( CSLCount(papszTokens) == 7 )
940 0 : nDepth = atoi(papszTokens[6]);
941 :
942 : /* -------------------------------------------------------------------- */
943 : /* What layer are we operating on. */
944 : /* -------------------------------------------------------------------- */
945 14 : OGRShapeLayer *poLayer = (OGRShapeLayer *) GetLayerByName(papszTokens[4]);
946 :
947 14 : if( poLayer == NULL )
948 : {
949 : CPLError( CE_Failure, CPLE_AppDefined,
950 : "Layer %s not recognised.",
951 0 : papszTokens[4] );
952 0 : CSLDestroy( papszTokens );
953 0 : return NULL;
954 : }
955 :
956 14 : CSLDestroy( papszTokens );
957 :
958 14 : poLayer->CreateSpatialIndex( nDepth );
959 14 : return NULL;
960 : }
961 :
962 :
963 : /************************************************************************/
964 : /* DeleteLayer() */
965 : /************************************************************************/
966 :
967 508 : OGRErr OGRShapeDataSource::DeleteLayer( int iLayer )
968 :
969 : {
970 : char *pszFilename;
971 :
972 : /* -------------------------------------------------------------------- */
973 : /* Verify we are in update mode. */
974 : /* -------------------------------------------------------------------- */
975 508 : if( !bDSUpdate )
976 : {
977 : CPLError( CE_Failure, CPLE_NoWriteAccess,
978 : "Data source %s opened read-only.\n"
979 : "Layer %d cannot be deleted.\n",
980 0 : pszName, iLayer );
981 :
982 0 : return OGRERR_FAILURE;
983 : }
984 :
985 508 : if( iLayer < 0 || iLayer >= nLayers )
986 : {
987 : CPLError( CE_Failure, CPLE_AppDefined,
988 : "Layer %d not in legal range of 0 to %d.",
989 0 : iLayer, nLayers-1 );
990 0 : return OGRERR_FAILURE;
991 : }
992 :
993 508 : OGRShapeLayer* poLayerToDelete = (OGRShapeLayer*) papoLayers[iLayer];
994 :
995 508 : pszFilename = CPLStrdup(poLayerToDelete->GetFullName());
996 :
997 508 : delete poLayerToDelete;
998 :
999 86968 : while( iLayer < nLayers - 1 )
1000 : {
1001 85952 : papoLayers[iLayer] = papoLayers[iLayer+1];
1002 85952 : iLayer++;
1003 : }
1004 :
1005 508 : nLayers--;
1006 :
1007 508 : VSIUnlink( CPLResetExtension(pszFilename, "shp") );
1008 508 : VSIUnlink( CPLResetExtension(pszFilename, "shx") );
1009 508 : VSIUnlink( CPLResetExtension(pszFilename, "dbf") );
1010 508 : VSIUnlink( CPLResetExtension(pszFilename, "prj") );
1011 508 : VSIUnlink( CPLResetExtension(pszFilename, "qix") );
1012 :
1013 508 : CPLFree( pszFilename );
1014 :
1015 508 : return OGRERR_NONE;
1016 : }
1017 :
1018 : /************************************************************************/
1019 : /* SetLastUsedLayer() */
1020 : /************************************************************************/
1021 :
1022 172764 : void OGRShapeDataSource::SetLastUsedLayer( OGRShapeLayer* poLayer )
1023 : {
1024 : /* We could remove that check and things would still work in */
1025 : /* 99.99% cases */
1026 : /* The only rationale for that test is to avoid breaking applications */
1027 : /* that would deal with layers of the same datasource in different */
1028 : /* threads. In GDAL < 1.9.0, this would work in most cases I can */
1029 : /* imagine as shapefile layers are pretty much independant from each */
1030 : /* others (although it has never been guaranteed to be a valid use case, */
1031 : /* and the shape driver is likely more the exception than the rule in */
1032 : /* permitting accessing layers from different threads !) */
1033 : /* Anyway the LRU list mechanism leaves the door open to concurrent accesses to it */
1034 : /* so when the datasource has not many layers, we don't try to build the */
1035 : /* LRU list to avoid concurrency issues. I haven't bothered making the analysis */
1036 : /* of how a mutex could be used to protect that (my intuition is that it would */
1037 : /* need to be placed at the beginning of OGRShapeLayer::TouchLayer() ) */
1038 172764 : if (nLayers < poPool->GetMaxSimultaneouslyOpened())
1039 165530 : return;
1040 :
1041 7234 : poPool->SetLastUsedLayer(poLayer);
1042 : }
|