~ubuntu-branches/ubuntu/quantal/qgis/quantal

« back to all changes in this revision

Viewing changes to providers/delimitedtext/qgsdelimitedtextprovider.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Steve Halasz
  • Date: 2004-12-21 09:46:27 UTC
  • Revision ID: james.westby@ubuntu.com-20041221094627-r9lb6mlz2o3yp8gn
Tags: upstream-0.6.0
ImportĀ upstreamĀ versionĀ 0.6.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/***************************************************************************
 
2
  qgsdelimitedtextprovider.cpp -  Data provider for delimted text
 
3
  -------------------
 
4
          begin                : 2004-02-27
 
5
          copyright            : (C) 2004 by Gary E.Sherman
 
6
          email                : sherman at mrcc.com
 
7
 ***************************************************************************/
 
8
 
 
9
/***************************************************************************
 
10
 *                                                                         *
 
11
 *   This program is free software; you can redistribute it and/or modify  *
 
12
 *   it under the terms of the GNU General Public License as published by  *
 
13
 *   the Free Software Foundation; either version 2 of the License, or     *
 
14
 *   (at your option) any later version.                                   *
 
15
 *                                                                         *
 
16
 ***************************************************************************/
 
17
/* $Id: qgsdelimitedtextprovider.cpp,v 1.18.2.1 2004/12/14 20:50:06 gsherman Exp $ */
 
18
 
 
19
#include <iostream>
 
20
#include <qfile.h>
 
21
#include <qtextstream.h>
 
22
#include <qstringlist.h>
 
23
#include <qmessagebox.h>
 
24
#include <qfiledialog.h>
 
25
#include <qsettings.h>
 
26
#include <qregexp.h>
 
27
#include <ogrsf_frmts.h>
 
28
#include "../../src/qgsdataprovider.h"
 
29
#include "../../src/qgsfeature.h"
 
30
#include "../../src/qgsfield.h"
 
31
#include "../../src/qgsrect.h"
 
32
#include "qgsdelimitedtextprovider.h"
 
33
#include <cfloat>
 
34
 
 
35
#ifdef WIN32
 
36
#define QGISEXTERN extern "C" __declspec( dllexport )
 
37
#else
 
38
#define QGISEXTERN extern "C"
 
39
#endif
 
40
 
 
41
  QgsDelimitedTextProvider::QgsDelimitedTextProvider(QString uri)
 
42
:mDataSourceUri(uri), mMinMaxCacheDirty(true)
 
43
{
 
44
  // Get the file name and mDelimiter out of the uri
 
45
  mFileName = uri.left(uri.find("?"));
 
46
  // split the string up on & to get the individual parameters
 
47
  QStringList parameters = QStringList::split("&", uri.mid(uri.find("?")));
 
48
#ifdef QGISDEBUG
 
49
  std::cerr << "Parameter count after split on &" << parameters.size() << std::endl; 
 
50
#endif
 
51
  // get the individual parameters and assign values
 
52
  QStringList temp = parameters.grep("delimiter=");
 
53
  mDelimiter = temp.size() ?temp[0].mid(temp[0].find("=") +1):"";
 
54
  temp = parameters.grep("xField=");
 
55
  mXField = temp.size() ?temp[0].mid(temp[0].find("=") +1):"";
 
56
  temp = parameters.grep("yField=");
 
57
  mYField = temp.size() ?temp[0].mid(temp[0].find("=") +1):"";
 
58
#ifdef QGISDEBUG
 
59
  std::cerr << "Data source uri is " << uri << std::endl;
 
60
  std::cerr << "Delimited text file is: " << mFileName << std::endl;
 
61
  std::cerr << "Delimiter is: " << mDelimiter << std::endl;
 
62
  std::cerr << "xField is: " << mXField << std::endl; 
 
63
  std::cerr << "yField is: " << mYField << std::endl; 
 
64
#endif
 
65
  // Set the selection rectangle to null
 
66
  mSelectionRectangle = 0;
 
67
  // assume the layer is invalid until proven otherwise
 
68
  mValid = false;
 
69
  if(!mFileName.isEmpty() && !mDelimiter.isEmpty() && !mXField.isEmpty() && !mYField.isEmpty()){
 
70
    // check to see that the file exists and perform some sanity checks
 
71
    if(QFile::exists(mFileName)){
 
72
      // Open the file and get number of rows, etc. We assume that the
 
73
      // file has a header row and process accordingly. Caller should make
 
74
      // sure the the delimited file is properly formed.
 
75
      mFile = new QFile(mFileName);
 
76
      if ( mFile->open( IO_ReadOnly ) ) {
 
77
        QTextStream stream( mFile );
 
78
        QString line;
 
79
        mNumberFeatures = 0;
 
80
        int xyCount = 0;
 
81
        int lineNumber = 0;
 
82
        // set the initial extent
 
83
        mExtent = new QgsRect(9999999999999.0,9999999999999.0,-9999999999999.0,-9999999999999.0);
 
84
        while ( !stream.atEnd() ) {
 
85
          lineNumber++;
 
86
          line = stream.readLine(); // line of text excluding '\n'
 
87
          if(mNumberFeatures++ == 0){
 
88
            // Get the fields from the header row and store them in the 
 
89
            // fields vector
 
90
#ifdef QGISDEBUG
 
91
            std::cerr << "Attempting to split the input line: " << line <<
 
92
              " using delimiter " << mDelimiter << std::endl;
 
93
#endif
 
94
            QStringList fieldList = QStringList::split(QRegExp(mDelimiter), line, true);
 
95
#ifdef QGISDEBUG
 
96
            std::cerr << "Split line into " << fieldList.size() << " parts" << std::endl; 
 
97
#endif
 
98
            // We don't know anything about a text based field other
 
99
            // than its name. All fields are assumed to be text
 
100
            int fieldPos = 0;
 
101
            for ( QStringList::Iterator it = fieldList.begin(); it != fieldList.end(); ++it ) 
 
102
            {
 
103
              QString field = *it;
 
104
              if(field.length() > 0)
 
105
              {
 
106
              attributeFields.push_back(QgsField(*it, "Text"));
 
107
              fieldPositions[*it] = fieldPos++;
 
108
              // check to see if this field matches either the x or y field
 
109
              if(mXField == *it)
 
110
              {
 
111
#ifdef QGISDEBUG
 
112
                std::cerr << "Found x field " <<  *it << std::endl; 
 
113
#endif
 
114
                xyCount++;
 
115
              }
 
116
              if(mYField == *it)
 
117
              {
 
118
#ifdef QGISDEBUG
 
119
                std::cerr << "Found y field " <<  *it << std::endl; 
 
120
#endif
 
121
                xyCount++;
 
122
              }
 
123
#ifdef QGISDEBUG
 
124
              std::cerr << "Adding field: " << *it << std::endl; 
 
125
#endif
 
126
 
 
127
            }
 
128
            }           
 
129
#ifdef QGISDEBUG 
 
130
            std::cerr << "Field count for the delimited text file is " << attributeFields.size() << std::endl; 
 
131
#endif 
 
132
          }else
 
133
          {
 
134
            // examine the x,y and update extents
 
135
            //  std::cout << line << std::endl; 
 
136
            // split the line on the delimiter
 
137
            QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
138
            //if(parts.size() == attributeFields.size())
 
139
            //{
 
140
            //  // we can populate attributes if required
 
141
            //  fieldsMatch = true;
 
142
            //}else
 
143
            //{
 
144
            //  fieldsMatch = false;
 
145
            //}
 
146
            /*
 
147
               std::cout << "Record hit line " << lineNumber << ": " <<
 
148
               parts[fieldPositions[mXField]] << ", " <<
 
149
               parts[fieldPositions[mYField]] << std::endl;
 
150
               */
 
151
            // Get the x and y values, first checking to make sure they
 
152
            // aren't null.
 
153
            QString sX = parts[fieldPositions[mXField]];
 
154
            QString sY = parts[fieldPositions[mYField]];
 
155
            //std::cout << "x ,y " << sX << ", " << sY << std::endl; 
 
156
            bool xOk = true;
 
157
            bool yOk = true;
 
158
            double x = sX.toDouble(&xOk);
 
159
            double y = sY.toDouble(&yOk);
 
160
            if(xOk && yOk)
 
161
            {
 
162
              if(x > mExtent->xMax())
 
163
              { 
 
164
                mExtent->setXmax(x);
 
165
              }
 
166
              if(x < mExtent->xMin())
 
167
              {
 
168
                mExtent->setXmin(x);
 
169
              }
 
170
              if(y > mExtent->yMax())
 
171
              {
 
172
                mExtent->setYmax(y);
 
173
              }
 
174
              if(y < mExtent->yMin())
 
175
              {
 
176
                mExtent->setYmin(y);
 
177
              }
 
178
            }
 
179
          }
 
180
        }
 
181
        reset();
 
182
        mNumberFeatures--;
 
183
 
 
184
        if(xyCount == 2)
 
185
        {
 
186
#ifdef QGISDEBUG
 
187
          std::cerr << "Data store is valid" << std::endl; 
 
188
          std::cerr << "Number of features " << mNumberFeatures << std::endl; 
 
189
          std::cerr << "Extents " << mExtent->stringRep() << std::endl; 
 
190
#endif
 
191
          mValid = true;
 
192
        }else
 
193
        {
 
194
          std::cerr << "Data store is invalid. Specified x,y fields do not match\n"
 
195
            << "those in the database (xyCount=" << xyCount << ")" << std::endl;
 
196
        }
 
197
      } 
 
198
 
 
199
#ifdef QGISDEBUG
 
200
      std::cerr << "Done checking validity\n";
 
201
#endif
 
202
 
 
203
      //resize the cache matrix
 
204
      mMinMaxCache=new double*[attributeFields.size()];
 
205
      for(int i=0;i<attributeFields.size();i++)
 
206
      {
 
207
        mMinMaxCache[i]=new double[2];
 
208
      }
 
209
    }else
 
210
      // file does not exist
 
211
      std::cerr << "Data source " << mDataSourceUri << " could not be opened" << std::endl; 
 
212
 
 
213
  }else
 
214
  {
 
215
    // uri is invalid so the layer must be too...
 
216
    std::cerr << "Data source is invalid" << std::endl;
 
217
 
 
218
  }
 
219
}
 
220
 
 
221
QgsDelimitedTextProvider::~QgsDelimitedTextProvider()
 
222
{
 
223
  mFile->close();
 
224
  delete mFile;
 
225
  for(int i=0;i<fieldCount();i++)
 
226
  {
 
227
    delete mMinMaxCache[i];
 
228
  }
 
229
  delete[] mMinMaxCache;
 
230
}
 
231
 
 
232
/**
 
233
 * Get the first feature resutling from a select operation
 
234
 * @return QgsFeature
 
235
 */
 
236
QgsFeature *QgsDelimitedTextProvider::getFirstFeature( bool fetchAttributes)
 
237
{
 
238
  QgsFeature *f = 0;
 
239
  if(mValid){
 
240
    /*
 
241
#ifdef QGISDEBUG
 
242
std::cerr << "getting first feature\n";
 
243
#endif
 
244
ogrLayer->ResetReading();
 
245
OGRFeature *feat = ogrLayer->GetNextFeature();
 
246
if(feat){
 
247
#ifdef QGISDEBUG
 
248
std::cerr << "First feature is not null\n";
 
249
#endif
 
250
}else{
 
251
#ifdef QGISDEBUG
 
252
std::cerr << "First feature is null\n";
 
253
#endif
 
254
}
 
255
f = new QgsFeature(feat->GetFID());
 
256
f->setGeometry(getGeometryPointer(feat));
 
257
if(fetchAttributes){
 
258
getFeatureAttributes(feat, f);
 
259
}
 
260
}
 
261
*/
 
262
  }
 
263
return f;
 
264
}
 
265
 
 
266
/**
 
267
 * Get the next feature resulting from a select operation
 
268
 * Return 0 if there are no features in the selection set
 
269
 * @return QgsFeature
 
270
 */
 
271
bool QgsDelimitedTextProvider::getNextFeature(QgsFeature &feature, bool fetchAttributes)
 
272
{
 
273
  // We must manually check each point to see if it is within the
 
274
  // selection rectangle
 
275
  bool returnValue;
 
276
 
 
277
  bool processPoint;
 
278
  if(mValid){
 
279
    // read the line
 
280
    QTextStream stream( mFile );
 
281
    QString line;
 
282
    if ( !stream.atEnd() ) {
 
283
#ifdef QGISDEBUG
 
284
      std::cerr << "Stream read" << std::endl; 
 
285
#endif
 
286
      line = stream.readLine(); // line of text excluding '\n'
 
287
      // create the geometry from the x, y fields
 
288
      QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
289
      // Get the x and y values, first checking to make sure they
 
290
      // aren't null.
 
291
      QString sX = parts[fieldPositions[mXField]];
 
292
      QString sY = parts[fieldPositions[mYField]];
 
293
      std::cerr << "x ,y " << sX << ", " << sY << std::endl; 
 
294
      bool xOk = true;
 
295
      bool yOk = true;
 
296
      double x = sX.toDouble(&xOk);
 
297
      double y = sY.toDouble(&yOk);
 
298
      if(xOk && yOk)
 
299
      {
 
300
        if(mSelectionRectangle == 0)
 
301
        {
 
302
          // no selection in place
 
303
          processPoint = true;
 
304
        }else
 
305
        {
 
306
          // check to see if point is in bounds
 
307
          processPoint = boundsCheck(x, y);
 
308
        }
 
309
        if(processPoint)
 
310
        {
 
311
          std::cerr << "Processing " << x << ", " << y << std::endl; 
 
312
          // create WKBPoint
 
313
          wkbPoint *geometry = new wkbPoint;
 
314
          geometry->byteOrder = endian();
 
315
          geometry->wkbType = 1;
 
316
          geometry->x = x;
 
317
          geometry->y = y;
 
318
          feature.setGeometry((unsigned char *)geometry, sizeof(wkbPoint));
 
319
          feature.setValid(true);
 
320
          // get the attributes if requested
 
321
          if(fetchAttributes){
 
322
            for(int fi =0; fi < attributeFields.size(); fi++)
 
323
            {
 
324
              feature.addAttribute(attributeFields.at(fi).name(), parts[fi]);
 
325
 
 
326
            }
 
327
 
 
328
            QString sX = parts[fieldPositions[mXField]];
 
329
          }
 
330
        }else
 
331
        {
 
332
          feature.setValid(false);
 
333
        }
 
334
 
 
335
      }
 
336
      // Return true since the read was successful. The feature itself
 
337
      // may be invalid for various reasons
 
338
      returnValue = true;
 
339
    }else
 
340
    {
 
341
#ifdef QGISDEBUG
 
342
      std::cerr << "Stream is at end" << std::endl; 
 
343
#endif
 
344
      // Return false since read of next feature failed
 
345
      returnValue = false;
 
346
      // Set the feature to invalid
 
347
      feature.setValid(false);
 
348
    }
 
349
 
 
350
  }
 
351
#ifdef QGISDEBUG
 
352
  QString sReturn = returnValue?"true":"false" ;
 
353
  std::cerr << "Returning " << sReturn << " from getNextFeature" << std::endl;
 
354
#endif
 
355
  return returnValue;
 
356
}
 
357
 
 
358
/**
 
359
 * Get the next feature resulting from a select operation
 
360
 * Return 0 if there are no features in the selection set
 
361
 * @return QgsFeature
 
362
 */
 
363
QgsFeature *QgsDelimitedTextProvider::getNextFeature(bool fetchAttributes)
 
364
{
 
365
  // We must manually check each point to see if it is within the
 
366
  // selection rectangle
 
367
  QgsFeature *f = 0;
 
368
  bool processPoint;
 
369
  if(mValid){
 
370
    // read the line
 
371
    QTextStream stream( mFile );
 
372
    QString line;
 
373
    if ( !stream.atEnd() ) {
 
374
      line = stream.readLine(); // line of text excluding '\n'
 
375
      // create the geometry from the x, y fields
 
376
      QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
377
      // Get the x and y values, first checking to make sure they
 
378
      // aren't null.
 
379
      QString sX = parts[fieldPositions[mXField]];
 
380
      QString sY = parts[fieldPositions[mYField]];
 
381
      //std::cout << "x ,y " << sX << ", " << sY << std::endl; 
 
382
      bool xOk = true;
 
383
      bool yOk = true;
 
384
      double x = sX.toDouble(&xOk);
 
385
      double y = sY.toDouble(&yOk);
 
386
      if(xOk && yOk)
 
387
      {
 
388
        if(mSelectionRectangle == 0)
 
389
        {
 
390
          // no selection in place
 
391
          processPoint = true;
 
392
        }else
 
393
        {
 
394
          // check to see if point is in bounds
 
395
          processPoint = boundsCheck(x, y);
 
396
          if(!processPoint)
 
397
          {
 
398
            // we need to continue to read until we get a hit in the
 
399
            // selection rectangle or the EOF is reached
 
400
            while(!stream.atEnd() && !processPoint)
 
401
            {
 
402
                ++mFid;
 
403
              line = stream.readLine();
 
404
 
 
405
              // create the geometry from the x, y fields
 
406
              parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
407
              // Get the x and y values, first checking to make sure they
 
408
              // aren't null.
 
409
              sX = parts[fieldPositions[mXField]];
 
410
              sY = parts[fieldPositions[mYField]];
 
411
              //std::cout << "x ,y " << sX << ", " << sY << std::endl; 
 
412
              xOk = true;
 
413
              yOk = true;
 
414
              x = sX.toDouble(&xOk);
 
415
              y = sY.toDouble(&yOk);
 
416
              if(xOk && yOk)
 
417
              {
 
418
                processPoint = boundsCheck(x, y);
 
419
              }
 
420
 
 
421
            }
 
422
          }
 
423
        }
 
424
        if(processPoint)
 
425
        {
 
426
          //std::cout << "Processing " << x << ", " << y << std::endl; 
 
427
          // create WKBPoint
 
428
          wkbPoint wkbPt;
 
429
          unsigned char * geometry = new unsigned char[sizeof(wkbPt)];
 
430
          geometry[0] = endian();
 
431
          int type = 1;
 
432
          void *ptr = geometry+1;
 
433
          memcpy((void*)(geometry +1), &type, 4);
 
434
          memcpy((void*)(geometry +5), &x, sizeof(x));
 
435
          memcpy((void*)(geometry +13), &y, sizeof(y));
 
436
          /*
 
437
             geometry->byteOrder = endian();
 
438
             geometry->wkbType = 1;
 
439
             geometry->x = x;
 
440
             geometry->y = y;
 
441
             */
 
442
          f = new QgsFeature();
 
443
          f->setGeometry(geometry, sizeof(wkbPt));
 
444
          //std::cerr << "Setting feature id to " << mFid << std::endl; 
 
445
          f->setFeatureId(mFid++);
 
446
          // get the attributes if requested
 
447
          if(fetchAttributes){
 
448
            // add the attributes to the attribute map
 
449
            for(int fi =0; fi < attributeFields.size(); fi++)
 
450
            {
 
451
              f->addAttribute(attributeFields.at(fi).name(), parts[fi]);
 
452
 
 
453
            }
 
454
 
 
455
          }
 
456
        }
 
457
      }
 
458
    }
 
459
 
 
460
  }
 
461
  return f;
 
462
}
 
463
 
 
464
QgsFeature * QgsDelimitedTextProvider::getNextFeature(std::list<int>& attlist)
 
465
{
 
466
     // We must manually check each point to see if it is within the
 
467
  // selection rectangle
 
468
  QgsFeature *f = 0;
 
469
  bool processPoint;
 
470
  if(mValid){
 
471
    // read the line
 
472
    QTextStream stream( mFile );
 
473
    QString line;
 
474
    if ( !stream.atEnd() ) {
 
475
      line = stream.readLine(); // line of text excluding '\n'
 
476
      // create the geometry from the x, y fields
 
477
      QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
478
      // Get the x and y values, first checking to make sure they
 
479
      // aren't null.
 
480
      QString sX = parts[fieldPositions[mXField]];
 
481
      QString sY = parts[fieldPositions[mYField]];
 
482
      //std::cout << "x ,y " << sX << ", " << sY << std::endl; 
 
483
      bool xOk = true;
 
484
      bool yOk = true;
 
485
      double x = sX.toDouble(&xOk);
 
486
      double y = sY.toDouble(&yOk);
 
487
      if(xOk && yOk)
 
488
      {
 
489
        if(mSelectionRectangle == 0)
 
490
        {
 
491
          // no selection in place
 
492
          processPoint = true;
 
493
        }else
 
494
        {
 
495
          // check to see if point is in bounds
 
496
          processPoint = boundsCheck(x, y);
 
497
          if(!processPoint)
 
498
          {
 
499
            // we need to continue to read until we get a hit in the
 
500
            // selection rectangle or the EOF is reached
 
501
            while(!stream.atEnd() && !processPoint)
 
502
            {
 
503
                ++mFid;
 
504
              line = stream.readLine();
 
505
 
 
506
              // create the geometry from the x, y fields
 
507
              parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
508
              // Get the x and y values, first checking to make sure they
 
509
              // aren't null.
 
510
              sX = parts[fieldPositions[mXField]];
 
511
              sY = parts[fieldPositions[mYField]];
 
512
              //std::cout << "x ,y " << sX << ", " << sY << std::endl; 
 
513
              xOk = true;
 
514
              yOk = true;
 
515
              x = sX.toDouble(&xOk);
 
516
              y = sY.toDouble(&yOk);
 
517
              if(xOk && yOk)
 
518
              {
 
519
                processPoint = boundsCheck(x, y);
 
520
              }
 
521
 
 
522
            }
 
523
          }
 
524
        }
 
525
        if(processPoint)
 
526
        {
 
527
          //std::cout << "Processing " << x << ", " << y << std::endl; 
 
528
          // create WKBPoint
 
529
          wkbPoint wkbPt;
 
530
          unsigned char * geometry = new unsigned char[sizeof(wkbPt)];
 
531
          geometry[0] = endian();
 
532
          int type = 1;
 
533
          void *ptr = geometry+1;
 
534
          memcpy((void*)(geometry +1), &type, 4);
 
535
          memcpy((void*)(geometry +5), &x, sizeof(x));
 
536
          memcpy((void*)(geometry +13), &y, sizeof(y));
 
537
          /*
 
538
             geometry->byteOrder = endian();
 
539
             geometry->wkbType = 1;
 
540
             geometry->x = x;
 
541
             geometry->y = y;
 
542
             */
 
543
          f = new QgsFeature();
 
544
          f->setGeometry(geometry, sizeof(wkbPt));
 
545
          //std::cerr << "Setting feature id to " << mFid << std::endl; 
 
546
          f->setFeatureId(mFid++);
 
547
          
 
548
          // add the attributes to the attribute map
 
549
              for(std::list<int>::iterator iter=attlist.begin();iter!=attlist.end();++iter)
 
550
              {
 
551
                  f->addAttribute(attributeFields.at(*iter).name(), parts[*iter]);
 
552
              }
 
553
        }
 
554
      }
 
555
    }
 
556
 
 
557
  }
 
558
  return f;
 
559
}
 
560
 
 
561
/**
 
562
 * Select features based on a bounding rectangle. Features can be retrieved
 
563
 * with calls to getFirstFeature and getNextFeature.
 
564
 * @param mbr QgsRect containing the extent to use in selecting features
 
565
 */
 
566
void QgsDelimitedTextProvider::select(QgsRect *rect, bool useIntersect)
 
567
{
 
568
 
 
569
  // Setting a spatial filter doesn't make much sense since we have to
 
570
  // compare each point against the rectangle.
 
571
  // We store the rect and use it in getNextFeature to determine if the
 
572
  // feature falls in the selection area
 
573
  mSelectionRectangle = new QgsRect((*rect));
 
574
  // Select implies an upcoming feature read so we reset the data source
 
575
  reset();
 
576
  // Reset the feature id to 0
 
577
  mFid = 0;
 
578
 
 
579
}
 
580
 
 
581
/**
 
582
 * Set the data source specification. This may be a path or database
 
583
 * connection string
 
584
 * @uri data source specification
 
585
 */
 
586
void QgsDelimitedTextProvider::setDataSourceUri(QString uri)
 
587
{
 
588
  mDataSourceUri = uri;
 
589
}
 
590
 
 
591
/**
 
592
 * Get the data source specification. This may be a path or database
 
593
 * connection string
 
594
 * @return data source specification
 
595
 */
 
596
QString QgsDelimitedTextProvider::getDataSourceUri()
 
597
{
 
598
  return mDataSourceUri;
 
599
}
 
600
 
 
601
/**
 
602
 * Identify features within the search radius specified by rect
 
603
 * @param rect Bounding rectangle of search radius
 
604
 * @return std::vector containing QgsFeature objects that intersect rect
 
605
 */
 
606
std::vector<QgsFeature>& QgsDelimitedTextProvider::identify(QgsRect * rect)
 
607
{
 
608
  // reset the data source since we need to be able to read through
 
609
  // all features
 
610
  reset();
 
611
  std::cerr << "Attempting to identify features falling within "
 
612
    << rect->stringRep() << std::endl; 
 
613
  // select the features
 
614
  select(rect);
 
615
#ifdef WIN32
 
616
  //TODO fix this later for win32
 
617
  std::vector<QgsFeature> feat;
 
618
  return feat;
 
619
#endif
 
620
 
 
621
}
 
622
 
 
623
/*
 
624
   unsigned char * QgsDelimitedTextProvider::getGeometryPointer(OGRFeature *fet){
 
625
   unsigned char *gPtr=0;
 
626
// get the wkb representation
 
627
 
 
628
//geom->exportToWkb((OGRwkbByteOrder) endian(), gPtr);
 
629
return gPtr;
 
630
 
 
631
}
 
632
*/
 
633
int QgsDelimitedTextProvider::endian()
 
634
{
 
635
  char *chkEndian = new char[4];
 
636
  memset(chkEndian, '\0', 4);
 
637
  chkEndian[0] = 0xE8;
 
638
 
 
639
  int *ce = (int *) chkEndian;
 
640
  int retVal;
 
641
  if (232 == *ce)
 
642
    retVal = NDR;
 
643
  else
 
644
    retVal = XDR;
 
645
  delete[]chkEndian;
 
646
  return retVal;
 
647
}
 
648
 
 
649
// Return the extent of the layer
 
650
QgsRect *QgsDelimitedTextProvider::extent()
 
651
{
 
652
  return new QgsRect(mExtent->xMin(), mExtent->yMin(), mExtent->xMax(), mExtent->yMax());
 
653
}
 
654
 
 
655
/** 
 
656
 * Return the feature type
 
657
 */
 
658
int QgsDelimitedTextProvider::geometryType(){
 
659
  return 1; // WKBPoint
 
660
}
 
661
/** 
 
662
 * Return the feature type
 
663
 */
 
664
long QgsDelimitedTextProvider::featureCount(){
 
665
  return mNumberFeatures;
 
666
}
 
667
 
 
668
/**
 
669
 * Return the number of fields
 
670
 */
 
671
int QgsDelimitedTextProvider::fieldCount(){
 
672
  return attributeFields.size();
 
673
}
 
674
/**
 
675
 * Fetch attributes for a selected feature
 
676
 */
 
677
void QgsDelimitedTextProvider::getFeatureAttributes(int key, QgsFeature *f){
 
678
  //for (int i = 0; i < ogrFet->GetFieldCount(); i++) {
 
679
 
 
680
  //  // add the feature attributes to the tree
 
681
  //  OGRFieldDefn *fldDef = ogrFet->GetFieldDefnRef(i);
 
682
  //  QString fld = fldDef->GetNameRef();
 
683
  //  //    OGRFieldType fldType = fldDef->GetType();
 
684
  //  QString val;
 
685
 
 
686
  //  val = ogrFet->GetFieldAsString(i);
 
687
  //  f->addAttribute(fld, val);
 
688
  //}
 
689
}
 
690
 
 
691
std::vector<QgsField>& QgsDelimitedTextProvider::fields(){
 
692
  return attributeFields;
 
693
}
 
694
 
 
695
void QgsDelimitedTextProvider::reset(){
 
696
  // Reset the file pointer to BOF
 
697
  mFile->reset();
 
698
  // Reset feature id to 0
 
699
  mFid = 0;
 
700
  // Skip ahead one line since first record is always assumed to be
 
701
  // the header record
 
702
  QTextStream stream( mFile );
 
703
  stream.readLine();
 
704
}
 
705
 
 
706
QString QgsDelimitedTextProvider::minValue(int position)
 
707
{
 
708
  if(position>=fieldCount())
 
709
  {
 
710
    std::cerr << "Warning: access requested to invalid position "
 
711
      << "in QgsDelimitedTextProvider::minValue(..)" << std::endl;
 
712
  }
 
713
  if(mMinMaxCacheDirty)
 
714
  {
 
715
    fillMinMaxCash();
 
716
  }
 
717
  return QString::number(mMinMaxCache[position][0],'f',2);
 
718
}
 
719
 
 
720
 
 
721
QString QgsDelimitedTextProvider::maxValue(int position)
 
722
{
 
723
  if(position>=fieldCount())
 
724
  {
 
725
    std::cerr << "Warning: access requested to invalid position "
 
726
      << "in QgsDelimitedTextProvider::maxValue(..)" << std::endl;
 
727
  }
 
728
  if(mMinMaxCacheDirty)
 
729
  {
 
730
    fillMinMaxCash();
 
731
  }
 
732
  return QString::number(mMinMaxCache[position][1],'f',2);
 
733
}
 
734
 
 
735
void QgsDelimitedTextProvider::fillMinMaxCash()
 
736
{
 
737
  for(int i=0;i<fieldCount();i++)
 
738
  {
 
739
    mMinMaxCache[i][0]=DBL_MAX;
 
740
    mMinMaxCache[i][1]=-DBL_MAX;
 
741
  }
 
742
 
 
743
  QgsFeature f;
 
744
  reset();
 
745
 
 
746
  getNextFeature(f, true);
 
747
  do
 
748
  {
 
749
    for(int i=0;i<fieldCount();i++)
 
750
    {
 
751
      double value=(f.attributeMap())[i].fieldValue().toDouble();
 
752
      if(value<mMinMaxCache[i][0])
 
753
      {
 
754
        mMinMaxCache[i][0]=value;  
 
755
      }  
 
756
      if(value>mMinMaxCache[i][1])
 
757
      {
 
758
        mMinMaxCache[i][1]=value;  
 
759
      }
 
760
    }
 
761
  }while(getNextFeature(f, true));
 
762
 
 
763
  mMinMaxCacheDirty=false;
 
764
}
 
765
//TODO - add sanity check for shape file layers, to include cheking to
 
766
//       see if the .shp, .dbf, .shx files are all present and the layer
 
767
//       actually has features
 
768
bool QgsDelimitedTextProvider::isValid(){
 
769
  return mValid;
 
770
}
 
771
 
 
772
/** 
 
773
 * Check to see if the point is within the selection rectangle
 
774
 */
 
775
bool QgsDelimitedTextProvider::boundsCheck(double x, double y)
 
776
{
 
777
  bool inBounds = (((x < mSelectionRectangle->xMax()) &&
 
778
        (x > mSelectionRectangle->xMin())) &&
 
779
      ((y < mSelectionRectangle->yMax()) &&
 
780
       (y > mSelectionRectangle->yMin())));
 
781
 // QString hit = inBounds?"true":"false";
 
782
 
 
783
 // std::cerr << "Checking if " << x << ", " << y << " is in " << 
 
784
 //mSelectionRectangle->stringRep().ascii() << ": " << hit.ascii() << std::endl; 
 
785
  return inBounds;
 
786
}
 
787
bool QgsDelimitedTextProvider::supportsSaveAsShapefile()
 
788
{
 
789
  return true;
 
790
}
 
791
 
 
792
bool QgsDelimitedTextProvider::saveAsShapefile()
 
793
{
 
794
  // save the layer as a shapefile
 
795
  QString driverName = "ESRI Shapefile";
 
796
  OGRSFDriver *poDriver;
 
797
  OGRRegisterAll();
 
798
  poDriver = OGRSFDriverRegistrar::GetRegistrar()->GetDriverByName((const char *)driverName );
 
799
  bool returnValue = true;
 
800
  if( poDriver != NULL )
 
801
  {
 
802
    // get a name for the shapefile
 
803
    // Get a file to process, starting at the current directory
 
804
    // Set inital dir to last used in delimited text plugin
 
805
    QSettings settings;
 
806
 
 
807
    QString shapefileName = QFileDialog::getSaveFileName(
 
808
        settings.readEntry("/Qgis/delimited_text_plugin/text_path","./"),
 
809
        "Shapefiles (*.shp)",
 
810
        0,
 
811
        "open file dialog",
 
812
        "Save delimited text layer as shapefile" );
 
813
 
 
814
    if(!shapefileName.isNull())
 
815
    {
 
816
      // add the extension if not present
 
817
      if(shapefileName.find(".shp") == -1)
 
818
      {
 
819
        shapefileName += ".shp";
 
820
      }
 
821
      OGRDataSource *poDS;
 
822
      // create the data source
 
823
      poDS = poDriver->CreateDataSource( (const char *)shapefileName, NULL );
 
824
      if( poDS != NULL )
 
825
      {
 
826
        std::cerr << "created datasource" << std::endl; 
 
827
        // datasource created, now create the output layer
 
828
        OGRLayer *poLayer;
 
829
        poLayer = poDS->CreateLayer((const char *)shapefileName.left(shapefileName.find(".shp")), NULL, static_cast<OGRwkbGeometryType>(1), NULL );
 
830
        if( poLayer != NULL )
 
831
        {
 
832
          std::cerr << "created layer" << std::endl; 
 
833
          // calculate the field lengths
 
834
          int *lengths = getFieldLengths();
 
835
          // create the fields
 
836
          std::cerr << "creating " << attributeFields.size() << " fields" << std::endl; 
 
837
          for(int i = 0; i < attributeFields.size(); i++)
 
838
          {
 
839
            // check the field length - if > 10 we need to truncate it
 
840
            QgsField attrField = attributeFields[i];
 
841
            if(attrField.name().length() > 10)
 
842
            {
 
843
              attrField = attrField.name().left(10);
 
844
            }
 
845
            // all fields are created as string (for now)
 
846
            OGRFieldDefn fld(attrField.name(), OFTString);
 
847
            // set the length for the field -- but we don't know what it is...
 
848
            fld.SetWidth(lengths[i]);
 
849
            // create the field
 
850
            std::cerr << "creating field " << attrField.name() << " width length " << lengths[i] << std::endl; 
 
851
            if(poLayer->CreateField(&fld) != OGRERR_NONE)
 
852
            {
 
853
              QMessageBox::warning(0, "Error", "Error creating field " + attrField.name());
 
854
            }
 
855
          }
 
856
          // read the delimited text file and create the features
 
857
          std::cerr << "Done creating fields" << std::endl; 
 
858
          // read the line
 
859
          reset();
 
860
          QTextStream stream( mFile );
 
861
          QString line;
 
862
          while ( !stream.atEnd() ) {
 
863
            line = stream.readLine(); // line of text excluding '\n'
 
864
            std::cerr << line << std::endl; 
 
865
            // split the line
 
866
            QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
867
            std::cerr << "Split line into " << parts.size() << std::endl; 
 
868
 
 
869
            // create the feature
 
870
            OGRFeature *poFeature;
 
871
 
 
872
            poFeature = new OGRFeature( poLayer->GetLayerDefn() );
 
873
 
 
874
            // iterate over the parts and set the fields
 
875
            std::cerr << "Setting the field values" << std::endl; 
 
876
            // set limit - we will ignore extra fields on the line
 
877
            int limit = attributeFields.size();
 
878
 
 
879
            if(parts.size() < limit){
 
880
            
 
881
              // this is bad - not enough values where supplied on the line
 
882
              // TODO We should inform the user about this...
 
883
            }
 
884
            else
 
885
            {
 
886
 
 
887
            for ( int i = 0; i < limit; i++ ) 
 
888
            {
 
889
              if(parts[i] != QString::null)
 
890
              {
 
891
                std::cerr << "Setting " << i << " "  << attributeFields[i].name() << " to " << parts[i] << std::endl; 
 
892
                poFeature->SetField(attributeFields[i].name(), parts[i]);
 
893
 
 
894
              }
 
895
              else
 
896
              {
 
897
                poFeature->SetField(attributeFields[i].name(), "");
 
898
              }
 
899
            }
 
900
            std::cerr << "Field values set" << std::endl; 
 
901
            // create the point
 
902
            OGRPoint *poPoint = new OGRPoint();
 
903
            QString sX = parts[fieldPositions[mXField]];
 
904
            QString sY = parts[fieldPositions[mYField]];
 
905
            poPoint->setX(sX.toDouble());
 
906
            poPoint->setY(sY.toDouble());
 
907
            std::cerr << "Setting geometry" << std::endl; 
 
908
 
 
909
            poFeature->SetGeometryDirectly(poPoint);
 
910
            if(poLayer->CreateFeature(poFeature) != OGRERR_NONE)
 
911
            {
 
912
              std::cerr << "Failed to create feature in shapefile" << std::endl; 
 
913
            }
 
914
            else
 
915
            {
 
916
              std::cerr << "Added feature" << std::endl; 
 
917
            }
 
918
 
 
919
            delete poFeature;
 
920
          }
 
921
          }
 
922
          delete poDS;
 
923
        }
 
924
        else
 
925
        {
 
926
          QMessageBox::warning(0,"Error", "Layer creation failed");
 
927
        }
 
928
 
 
929
      }
 
930
      else
 
931
      {
 
932
        QMessageBox::warning(0, "Error creating shapefile", 
 
933
            "The shapefile could not be created (" + shapefileName + ")");
 
934
      }
 
935
 
 
936
    }
 
937
    //std::cerr << "Saving to " << shapefileName << std::endl; 
 
938
  }
 
939
  else
 
940
  {
 
941
    QMessageBox::warning(0, "Driver not found", driverName +  " driver is not available");
 
942
    returnValue = false;
 
943
  }
 
944
  return returnValue;
 
945
}
 
946
 
 
947
 
 
948
int* QgsDelimitedTextProvider::getFieldLengths()
 
949
{
 
950
  // this function parses the entire data file and calculates the
 
951
  // max for each
 
952
 
 
953
  // Only do this if we haven't done it already (ie. the vector is
 
954
  // empty)
 
955
  int *lengths = new int[attributeFields.size()];
 
956
  // init the lengths to zero
 
957
  for(int il=0; il < attributeFields.size(); il++)
 
958
  {
 
959
    lengths[il] = 0;
 
960
  }
 
961
  if(mValid){
 
962
    reset();
 
963
    // read the line
 
964
    QTextStream stream( mFile );
 
965
    QString line;
 
966
    while ( !stream.atEnd() ) {
 
967
      line = stream.readLine(); // line of text excluding '\n'
 
968
      // split the line
 
969
      QStringList parts = QStringList::split(QRegExp(mDelimiter), line, true);
 
970
      // iterate over the parts and update the max value
 
971
      for ( int i = 0; i < parts.size(); i++ ) 
 
972
      {
 
973
       if(parts[i] != QString::null)
 
974
       { 
 
975
       // std::cerr << "comparing length for " << parts[i] << " against max len of " << lengths[i] << std::endl; 
 
976
        if(parts[i].length() > lengths[i])
 
977
        {
 
978
          lengths[i] = parts[i].length();
 
979
        }
 
980
       }
 
981
 
 
982
      }
 
983
    }
 
984
  }
 
985
  return lengths;
 
986
}
 
987
/**
 
988
 * Class factory to return a pointer to a newly created 
 
989
 * QgsDelimitedTextProvider object
 
990
 */
 
991
QGISEXTERN QgsDelimitedTextProvider * classFactory(const char *uri)
 
992
{
 
993
  return new QgsDelimitedTextProvider(uri);
 
994
}
 
995
/** Required key function (used to map the plugin to a data store type)
 
996
*/
 
997
QGISEXTERN QString providerKey(){
 
998
  return QString("delimitedtext");
 
999
}
 
1000
/**
 
1001
 * Required description function 
 
1002
 */
 
1003
QGISEXTERN QString description(){
 
1004
  return QString("Delimited text data provider");
 
1005
 
1006
/**
 
1007
 * Required isProvider function. Used to determine if this shared library
 
1008
 * is a data provider plugin
 
1009
 */
 
1010
QGISEXTERN bool isProvider(){
 
1011
  return true;
 
1012
}
 
1013