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

« back to all changes in this revision

Viewing changes to src/providers/delimitedtext/qgsdelimitedtextprovider.cpp

  • Committer: Bazaar Package Importer
  • Author(s): William Grant
  • Date: 2007-05-06 13:42:32 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20070506134232-pyli6t388w5asd8x
Tags: 0.8.0-3ubuntu1
* Merge from Debian unstable. Remaining Ubuntu changes:
  - debian/rules, debian/qgis.install, debian/qgis.dirs debian/qgis.desktop:
    Add and install .desktop.
* debian/qgis.desktop: Remove Applications category; it's not real.
* Modify Maintainer value to match Debian-Maintainer-Field Spec

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