delimited_text_enhancement.patch

Patch file to implement delimited text provider enhancement - Chris Crook, 2010-11-28 11:58 AM

Download (38.2 KB)

View differences:

src/plugins/delimited_text/qgsdelimitedtextplugin.cpp (working copy)
102 102
  setCurrentTheme( "" );
103 103
  myQActionPointer->setWhatsThis( tr( "Add a delimited text file as a map layer. "
104 104
                                      "The file must have a header row containing the field names. "
105
                                      "X and Y fields are required and must contain coordinates in decimal units." ) );
105
                                      "The file must either contain X and Y fields with coordinates in decimal units or a WKT field." ) );
106 106
  // Connect the action to the run
107 107
  connect( myQActionPointer, SIGNAL( triggered() ), this, SLOT( run() ) );
108 108
  // Add the icon to the toolbar
src/plugins/delimited_text/qgsdelimitedtextplugingui.cpp (working copy)
33 33
  setupUi( this );
34 34
  pbnOK = buttonBox->button( QDialogButtonBox::Ok );
35 35

  
36
  enableAccept();
36
  updateFieldsAndEnable();
37 37

  
38 38
  // at startup, fetch the last used delimiter and directory from
39 39
  // settings
......
58 58

  
59 59
  cmbXField->setDisabled( true );
60 60
  cmbYField->setDisabled( true );
61
  cmbWktField->setDisabled( true );
61 62

  
62
  connect( txtFilePath, SIGNAL( textChanged( QString ) ), this, SLOT( enableAccept() ) );
63
  connect( txtFilePath, SIGNAL( textChanged( QString ) ), this, SLOT( updateFieldsAndEnable() ) );
63 64

  
64
  connect( delimiterSelection, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) );
65
  connect( delimiterPlain, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) );
66
  connect( delimiterRegexp, SIGNAL( toggled( bool ) ), this, SLOT( enableAccept() ) );
65
  connect( delimiterSelection, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) );
66
  connect( delimiterPlain, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) );
67
  connect( delimiterRegexp, SIGNAL( toggled( bool ) ), this, SLOT( updateFieldsAndEnable() ) );
67 68

  
68
  connect( cbxDelimSpace, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) );
69
  connect( cbxDelimTab, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) );
70
  connect( cbxDelimSemicolon, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) );
71
  connect( cbxDelimComma, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) );
72
  connect( cbxDelimColon, SIGNAL( stateChanged( int ) ), this, SLOT( enableAccept() ) );
69
  connect( cbxDelimSpace, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
70
  connect( cbxDelimTab, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
71
  connect( cbxDelimSemicolon, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
72
  connect( cbxDelimComma, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
73
  connect( cbxDelimColon, SIGNAL( stateChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
73 74

  
74
  connect( txtDelimiter, SIGNAL( editingFinished() ), this, SLOT( enableAccept() ) );
75
  connect( txtDelimiter, SIGNAL( editingFinished() ), this, SLOT( updateFieldsAndEnable() ) );
75 76

  
76
  connect( rowCounter, SIGNAL( valueChanged( int ) ), this, SLOT( enableAccept() ) );
77
  connect( rowCounter, SIGNAL( valueChanged( int ) ), this, SLOT( updateFieldsAndEnable() ) );
77 78
}
78 79

  
79 80
QgsDelimitedTextPluginGui::~QgsDelimitedTextPluginGui()
......
103 104
                  .arg( txtDelimiter->text() )
104 105
                  .arg( delimiterType );
105 106

  
106
    if ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() )
107
    {
108
      uri += QString( "&xField=%1&yField=%2" )
109
             .arg( cmbXField->currentText() )
110
             .arg( cmbYField->currentText() );
111
    }
107
	if( geomTypeXY->isChecked())
108
	{
109
		if ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() )
110
		{
111
		  uri += QString( "&xField=%1&yField=%2" )
112
				 .arg( cmbXField->currentText() )
113
				 .arg( cmbYField->currentText() );
114
		}
115
	}
116
	else
117
	{
118
		if( ! cmbWktField->currentText().isEmpty() )
119
		{
120
		  uri += QString( "&wktField=%1" )
121
				 .arg( cmbWktField->currentText() );
122
		}
123
	}
112 124

  
113 125
    int skipLines = rowCounter->value();
114 126
    if ( skipLines > 0 )
......
177 189
  return fieldList;
178 190
}
179 191

  
192
bool QgsDelimitedTextPluginGui::haveValidFileAndDelimiters()
193
{
194

  
195
  bool valid = true;
196
  // If there is no valid file or field delimiters than cannot determine fields
197
  if ( txtFilePath->text().isEmpty() || !QFile( txtFilePath->text() ).exists() )
198
  {
199
    valid = false;
200
  }
201
  else if ( delimiterSelection->isChecked() )
202
  {
203
    valid =
204
      cbxDelimSpace->isChecked() ||
205
      cbxDelimTab->isChecked() ||
206
      cbxDelimSemicolon->isChecked() ||
207
      cbxDelimComma->isChecked() ||
208
      cbxDelimColon->isChecked();
209
  }
210
  else
211
  {
212
    valid = !txtDelimiter->text().isEmpty();
213
  }
214
  return valid;
215
}
216

  
180 217
void QgsDelimitedTextPluginGui::updateFieldLists()
181 218
{
182 219
  // Update the x and y field dropdown boxes
......
184 221

  
185 222
  disconnect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
186 223
  disconnect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
224
  disconnect( cmbWktField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
225
  disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbXField, SLOT(setEnabled(bool)));
226
  disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbYField, SLOT(setEnabled(bool)));
227
  disconnect(geomTypeXY, SIGNAL(toggled(bool)), cmbWktField, SLOT(setDisabled(bool)));
187 228

  
188 229
  QString columnX = cmbXField->currentText();
189 230
  QString columnY = cmbYField->currentText();
231
  QString columnWkt = cmbWktField->currentText();
190 232

  
191 233
  // clear the field lists
192 234
  cmbXField->clear();
193 235
  cmbYField->clear();
236
  cmbWktField->clear();
194 237

  
238
  geomTypeXY->setEnabled( false );
239
  geomTypeWKT->setEnabled( false );
195 240
  cmbXField->setEnabled( false );
196 241
  cmbYField->setEnabled( false );
242
  cmbWktField->setEnabled( false );
197 243

  
244
  if( ! haveValidFileAndDelimiters()) return;
245

  
198 246
  QFile file( txtFilePath->text() );
199 247
  if ( !file.open( QIODevice::ReadOnly ) )
200 248
    return;
......
220 268
  //
221 269
  // We don't know anything about a text based field other
222 270
  // than its name. All fields are assumed to be text
271
  bool haveFields = false;
272

  
223 273
  foreach( QString field, fieldList )
224 274
  {
225 275
    if (( field.left( 1 ) == "'" || field.left( 1 ) == "\"" ) &&
......
233 283

  
234 284
    cmbXField->addItem( field );
235 285
    cmbYField->addItem( field );
286
	cmbWktField->addItem( field );
287
	haveFields = true;
236 288
  }
237 289

  
238
  cmbXField->setEnabled( cmbXField->count() > 0 );
239
  cmbYField->setEnabled( cmbYField->count() > 0 );
290
  int indexWkt = -1;
291
  if( ! columnWkt.isEmpty() )
292
  {
293
	  indexWkt = cmbWktField->findText( columnWkt );
294
  }
295
  if( indexWkt < 0 )
296
  {
297
	  indexWkt = cmbWktField->findText("wkt", Qt::MatchContains );
298
  }
299
  if( indexWkt < 0 )
300
  {
301
	  indexWkt = cmbWktField->findText("geometry", Qt::MatchContains );
302
  }
303
  if( indexWkt < 0 )
304
  {
305
	  indexWkt = cmbWktField->findText("shape", Qt::MatchContains );
306
  }
307
  cmbWktField->setCurrentIndex( indexWkt);
240 308

  
241 309
  int indexX = -1;
242 310
  if ( !columnX.isEmpty() )
......
274 342

  
275 343
  cmbYField->setCurrentIndex( indexY );
276 344

  
277
  connect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
278
  connect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
279 345

  
346
  bool isXY = (geomTypeXY->isChecked() && indexX >= 0 && indexY >= 0) || indexWkt < 0;
347

  
348
  geomTypeXY->setChecked(  isXY );
349
  geomTypeWKT->setChecked( ! isXY );
350

  
351
  if( haveFields )
352
  {
353
	  geomTypeXY->setEnabled(true);
354
	  geomTypeWKT->setEnabled(true);
355
	  cmbXField->setEnabled( isXY );
356
	  cmbYField->setEnabled( isXY );
357
	  cmbWktField->setEnabled( !  isXY );
358

  
359

  
360
	  connect( cmbXField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
361
	  connect( cmbYField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
362
	  connect( cmbWktField, SIGNAL( currentIndexChanged( int ) ), this, SLOT( enableAccept() ) );
363
	  connect(geomTypeXY, SIGNAL(toggled(bool)), cmbXField, SLOT(setEnabled(bool)));
364
	  connect(geomTypeXY, SIGNAL(toggled(bool)), cmbYField, SLOT(setEnabled(bool)));
365
	  connect(geomTypeXY, SIGNAL(toggled(bool)), cmbWktField, SLOT(setDisabled(bool)));
366
  }
367

  
280 368
  // clear the sample text box
281 369
  tblSample->clear();
282 370

  
......
324 412
  txtFilePath->setText( s );
325 413
}
326 414

  
415
void QgsDelimitedTextPluginGui::updateFieldsAndEnable()
416
{
417
	updateFieldLists();
418
	enableAccept();
419
}
420

  
327 421
void QgsDelimitedTextPluginGui::enableAccept()
328 422
{
329
  bool enabled = false;
330 423

  
331
  if ( txtFilePath->text().isEmpty() || !QFile( txtFilePath->text() ).exists() )
332
  {
333
    enabled = false;
334
  }
335
  else if ( delimiterSelection->isChecked() )
336
  {
337
    enabled =
338
      cbxDelimSpace->isChecked() ||
339
      cbxDelimTab->isChecked() ||
340
      cbxDelimSemicolon->isChecked() ||
341
      cbxDelimComma->isChecked() ||
342
      cbxDelimColon->isChecked();
343
  }
344
  else
345
  {
346
    enabled = !txtDelimiter->text().isEmpty();
347
  }
424
  // If the geometry type field is enabled then there must be 
425
  // a valid file, and it must be 
426
  bool enabled = haveValidFileAndDelimiters();
348 427

  
349

  
350 428
  if ( enabled )
351 429
  {
352
    updateFieldLists();
353 430

  
354
    enabled = ( cmbXField->currentText().isEmpty() && cmbYField->currentText().isEmpty() )
355
              || ( !cmbXField->currentText().isEmpty() && !cmbYField->currentText().isEmpty() && cmbXField->currentText() != cmbYField->currentText() );
431
	if( geomTypeXY->isChecked() )
432
	{
433
       enabled = !( cmbXField->currentText().isEmpty()  || cmbYField->currentText().isEmpty() || cmbXField->currentText() == cmbYField->currentText() );
434
	}
435
	else
436
	{
437
       enabled = !cmbWktField->currentText().isEmpty();
438
	}
356 439
  }
357 440

  
358 441
  pbnOK->setEnabled( enabled );
src/plugins/delimited_text/qgsdelimitedtextplugingui.h (working copy)
34 34
    QStringList splitLine( QString line );
35 35

  
36 36
  private:
37
    bool haveValidFileAndDelimiters();
37 38
    void updateFieldLists();
38 39
    void getOpenFileName();
39 40

  
......
47 48
    void on_btnBrowseForFile_clicked();
48 49

  
49 50
  public slots:
51
    void updateFieldsAndEnable();
50 52
    void enableAccept();
51 53

  
52 54
  signals:
src/plugins/delimited_text/qgsdelimitedtextpluginguibase.ui (working copy)
6 6
   <rect>
7 7
    <x>0</x>
8 8
    <y>0</y>
9
    <width>532</width>
10
    <height>513</height>
9
    <width>589</width>
10
    <height>522</height>
11 11
   </rect>
12 12
  </property>
13 13
  <property name="windowTitle">
......
110 110
     <property name="enabled">
111 111
      <bool>true</bool>
112 112
     </property>
113
     <layout class="QGridLayout" name="gridLayout_5">
113
     <layout class="QGridLayout" name="gridLayout_5" columnstretch="1,2">
114 114
      <item row="0" column="0">
115 115
       <widget class="QRadioButton" name="delimiterSelection">
116 116
        <property name="text">
......
121 121
        </property>
122 122
       </widget>
123 123
      </item>
124
      <item row="2" column="1">
125
       <widget class="QCheckBox" name="cbxDelimSemicolon">
126
        <property name="text">
127
         <string>Semicolon</string>
128
        </property>
129
       </widget>
130
      </item>
131
      <item row="1" column="1">
132
       <widget class="QCheckBox" name="cbxDelimTab">
133
        <property name="text">
134
         <string>Tab</string>
135
        </property>
136
       </widget>
137
      </item>
138
      <item row="0" column="1">
139
       <widget class="QCheckBox" name="cbxDelimSpace">
140
        <property name="text">
141
         <string>Space</string>
142
        </property>
143
        <property name="checked">
144
         <bool>true</bool>
145
        </property>
146
       </widget>
147
      </item>
148
      <item row="3" column="1">
149
       <widget class="QCheckBox" name="cbxDelimComma">
150
        <property name="text">
151
         <string>Comma</string>
152
        </property>
153
       </widget>
154
      </item>
155
      <item row="4" column="1">
156
       <widget class="QCheckBox" name="cbxDelimColon">
157
        <property name="text">
158
         <string>Colon</string>
159
        </property>
160
       </widget>
161
      </item>
162
      <item row="4" column="0">
163
       <widget class="QLineEdit" name="txtDelimiter">
164
        <property name="enabled">
165
         <bool>false</bool>
166
        </property>
167
        <property name="sizePolicy">
168
         <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
169
          <horstretch>0</horstretch>
170
          <verstretch>0</verstretch>
171
         </sizepolicy>
172
        </property>
173
        <property name="maximumSize">
174
         <size>
175
          <width>32767</width>
176
          <height>32767</height>
177
         </size>
178
        </property>
179
        <property name="toolTip">
180
         <string>Delimiter to use when splitting fields in the text file. The delimiter can be more than one character.</string>
181
        </property>
182
        <property name="whatsThis">
183
         <string>Delimiter to use when splitting fields in the delimited text file. The delimiter can be 1 or more characters in length.</string>
184
        </property>
185
       </widget>
186
      </item>
187
      <item row="3" column="0">
124
      <item row="5" column="0">
188 125
       <widget class="QRadioButton" name="delimiterRegexp">
189 126
        <property name="sizePolicy">
190 127
         <sizepolicy hsizetype="Minimum" vsizetype="Minimum">
......
222 159
        </property>
223 160
       </widget>
224 161
      </item>
162
      <item row="0" column="1">
163
       <layout class="QGridLayout" name="gridLayout">
164
        <item row="0" column="0">
165
         <widget class="QCheckBox" name="cbxDelimTab">
166
          <property name="text">
167
           <string>Tab</string>
168
          </property>
169
         </widget>
170
        </item>
171
        <item row="0" column="1">
172
         <widget class="QCheckBox" name="cbxDelimSpace">
173
          <property name="text">
174
           <string>Space</string>
175
          </property>
176
          <property name="checked">
177
           <bool>true</bool>
178
          </property>
179
         </widget>
180
        </item>
181
        <item row="1" column="0">
182
         <widget class="QCheckBox" name="cbxDelimComma">
183
          <property name="text">
184
           <string>Comma</string>
185
          </property>
186
         </widget>
187
        </item>
188
        <item row="1" column="1">
189
         <widget class="QCheckBox" name="cbxDelimSemicolon">
190
          <property name="text">
191
           <string>Semicolon</string>
192
          </property>
193
         </widget>
194
        </item>
195
        <item row="1" column="2">
196
         <widget class="QCheckBox" name="cbxDelimColon">
197
          <property name="text">
198
           <string>Colon</string>
199
          </property>
200
         </widget>
201
        </item>
202
       </layout>
203
      </item>
204
      <item row="2" column="1">
205
       <widget class="QLineEdit" name="txtDelimiter">
206
        <property name="enabled">
207
         <bool>false</bool>
208
        </property>
209
        <property name="sizePolicy">
210
         <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
211
          <horstretch>0</horstretch>
212
          <verstretch>0</verstretch>
213
         </sizepolicy>
214
        </property>
215
        <property name="maximumSize">
216
         <size>
217
          <width>32767</width>
218
          <height>32767</height>
219
         </size>
220
        </property>
221
        <property name="toolTip">
222
         <string>Delimiter to use when splitting fields in the text file. The delimiter can be more than one character.</string>
223
        </property>
224
        <property name="whatsThis">
225
         <string>Delimiter to use when splitting fields in the delimited text file. The delimiter can be 1 or more characters in length.</string>
226
        </property>
227
       </widget>
228
      </item>
225 229
     </layout>
226 230
    </widget>
227 231
   </item>
......
236 240
       <verstretch>0</verstretch>
237 241
      </sizepolicy>
238 242
     </property>
239
     <layout class="QGridLayout" name="gridLayout_6">
243
     <layout class="QGridLayout" name="gridLayout_6" columnstretch="1,2">
240 244
      <item row="1" column="1">
241 245
       <widget class="QSpinBox" name="rowCounter">
242 246
        <property name="minimumSize">
......
276 280
    </widget>
277 281
   </item>
278 282
   <item>
279
    <widget class="QWidget" name="widget_2" native="true">
280
     <layout class="QGridLayout" name="gridLayout_2">
281
      <item row="0" column="2">
283
    <widget class="QFrame" name="gridFrame_2">
284
     <layout class="QGridLayout" name="gridLayout_7" columnstretch="1,1,4">
285
      <property name="margin">
286
       <number>9</number>
287
      </property>
288
      <item row="4" column="0">
289
       <widget class="QRadioButton" name="geomTypeWKT">
290
        <property name="enabled">
291
         <bool>false</bool>
292
        </property>
293
        <property name="whatsThis">
294
         <string>The file contains a well known text geometry field</string>
295
        </property>
296
        <property name="text">
297
         <string>WKT field</string>
298
        </property>
299
       </widget>
300
      </item>
301
      <item row="2" column="1">
282 302
       <widget class="QLabel" name="textLabelx">
283 303
        <property name="text">
284 304
         <string>&lt;p align=&quot;right&quot;&gt;X field&lt;/p&gt;</string>
285 305
        </property>
286 306
       </widget>
287 307
      </item>
288
      <item row="0" column="3">
289
       <widget class="QComboBox" name="cmbXField">
308
      <item row="2" column="2">
309
       <layout class="QHBoxLayout" name="horizontalLayout">
310
        <item>
311
         <widget class="QComboBox" name="cmbXField">
312
          <property name="enabled">
313
           <bool>false</bool>
314
          </property>
315
          <property name="sizePolicy">
316
           <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
317
            <horstretch>0</horstretch>
318
            <verstretch>0</verstretch>
319
           </sizepolicy>
320
          </property>
321
          <property name="minimumSize">
322
           <size>
323
            <width>120</width>
324
            <height>0</height>
325
           </size>
326
          </property>
327
          <property name="toolTip">
328
           <string>Name of the field containing x values</string>
329
          </property>
330
          <property name="whatsThis">
331
           <string>Name of the field containing x values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file.</string>
332
          </property>
333
          <property name="editable">
334
           <bool>true</bool>
335
          </property>
336
         </widget>
337
        </item>
338
       </layout>
339
      </item>
340
      <item row="2" column="0">
341
       <widget class="QRadioButton" name="geomTypeXY">
342
        <property name="enabled">
343
         <bool>false</bool>
344
        </property>
345
        <property name="whatsThis">
346
         <string>The file contains X and Y coordinate columns</string>
347
        </property>
348
        <property name="text">
349
         <string>X Y fields</string>
350
        </property>
351
        <property name="checked">
352
         <bool>true</bool>
353
        </property>
354
       </widget>
355
      </item>
356
      <item row="3" column="2">
357
       <widget class="QComboBox" name="cmbYField">
358
        <property name="enabled">
359
         <bool>false</bool>
360
        </property>
290 361
        <property name="sizePolicy">
291 362
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
292 363
          <horstretch>0</horstretch>
......
300 371
         </size>
301 372
        </property>
302 373
        <property name="toolTip">
303
         <string>Name of the field containing x values</string>
374
         <string>Name of the field containing y values</string>
304 375
        </property>
305 376
        <property name="whatsThis">
306
         <string>Name of the field containing x values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file.</string>
377
         <string>Name of the field containing y values. Choose a field from the list. The list is generated by parsing the header row of the delimited text file.</string>
307 378
        </property>
308 379
        <property name="editable">
309 380
         <bool>true</bool>
310 381
        </property>
311 382
       </widget>
312 383
      </item>
313
      <item row="0" column="5">
384
      <item row="3" column="1">
314 385
       <widget class="QLabel" name="textLabely">
315 386
        <property name="text">
316 387
         <string>&lt;p align=&quot;right&quot;&gt;Y field&lt;/p&gt;</string>
317 388
        </property>
318 389
       </widget>
319 390
      </item>
320
      <item row="0" column="6">
321
       <widget class="QComboBox" name="cmbYField">
391
      <item row="4" column="2">
392
       <widget class="QComboBox" name="cmbWktField">
393
        <property name="enabled">
394
         <bool>false</bool>
395
        </property>
322 396
        <property name="sizePolicy">
323 397
         <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
324 398
          <horstretch>0</horstretch>
......
376 450
  </customwidget>
377 451
 </customwidgets>
378 452
 <tabstops>
453
  <tabstop>txtFilePath</tabstop>
454
  <tabstop>btnBrowseForFile</tabstop>
455
  <tabstop>txtLayerName</tabstop>
456
  <tabstop>delimiterSelection</tabstop>
457
  <tabstop>cbxDelimTab</tabstop>
458
  <tabstop>cbxDelimSpace</tabstop>
459
  <tabstop>cbxDelimComma</tabstop>
460
  <tabstop>cbxDelimSemicolon</tabstop>
461
  <tabstop>cbxDelimColon</tabstop>
462
  <tabstop>delimiterPlain</tabstop>
463
  <tabstop>delimiterRegexp</tabstop>
464
  <tabstop>txtDelimiter</tabstop>
465
  <tabstop>rowCounter</tabstop>
466
  <tabstop>geomTypeXY</tabstop>
379 467
  <tabstop>cmbXField</tabstop>
380 468
  <tabstop>cmbYField</tabstop>
469
  <tabstop>geomTypeWKT</tabstop>
470
  <tabstop>cmbWktField</tabstop>
471
  <tabstop>tblSample</tabstop>
381 472
  <tabstop>buttonBox</tabstop>
382 473
 </tabstops>
383 474
 <resources>
......
395 486
     <y>121</y>
396 487
    </hint>
397 488
    <hint type="destinationlabel">
398
     <x>282</x>
399
     <y>115</y>
489
     <x>445</x>
490
     <y>113</y>
400 491
    </hint>
401 492
   </hints>
402 493
  </connection>
......
411 502
     <y>110</y>
412 503
    </hint>
413 504
    <hint type="destinationlabel">
414
     <x>277</x>
415
     <y>149</y>
505
     <x>286</x>
506
     <y>113</y>
416 507
    </hint>
417 508
   </hints>
418 509
  </connection>
......
427 518
     <y>109</y>
428 519
    </hint>
429 520
    <hint type="destinationlabel">
430
     <x>281</x>
431
     <y>175</y>
521
     <x>445</x>
522
     <y>138</y>
432 523
    </hint>
433 524
   </hints>
434 525
  </connection>
......
443 534
     <y>114</y>
444 535
    </hint>
445 536
    <hint type="destinationlabel">
446
     <x>293</x>
447
     <y>203</y>
537
     <x>286</x>
538
     <y>138</y>
448 539
    </hint>
449 540
   </hints>
450 541
  </connection>
......
459 550
     <y>114</y>
460 551
    </hint>
461 552
    <hint type="destinationlabel">
462
     <x>118</x>
463
     <y>218</y>
553
     <x>397</x>
554
     <y>165</y>
464 555
    </hint>
465 556
   </hints>
466 557
  </connection>
......
475 566
     <y>113</y>
476 567
    </hint>
477 568
    <hint type="destinationlabel">
478
     <x>301</x>
479
     <y>225</y>
569
     <x>531</x>
570
     <y>138</y>
480 571
    </hint>
481 572
   </hints>
482 573
  </connection>
src/providers/delimitedtext/qgsdelimitedtextprovider.cpp (working copy)
34 34
#include "qgsdataprovider.h"
35 35
#include "qgsfeature.h"
36 36
#include "qgsfield.h"
37
#include "qgsgeometry.h"
37 38
#include "qgslogger.h"
38 39
#include "qgsmessageoutput.h"
39 40
#include "qgsrectangle.h"
......
132 133

  
133 134
QgsDelimitedTextProvider::QgsDelimitedTextProvider( QString uri )
134 135
    : QgsVectorDataProvider( uri ),
135
    mXFieldIndex( -1 ), mYFieldIndex( -1 ),
136
    mShowInvalidLines( true ), mWkbType( QGis::WKBPoint )
136
	mHasWktField( false ), mFieldCount(0),
137
    mXFieldIndex( -1 ), mYFieldIndex( -1 ), 
138
	mWktFieldIndex( -1 ), mWktHasZM( false),
139
	mWktZMRegexp("\\s+(?:z|m|zm)(?=\\s*\\()",Qt::CaseInsensitive),
140
	mWktCrdRegexp("(\\-?\\d+(?:\\.\\d*)?\\s+\\-?\\d+(?:\\.\\d*)?)\\s[\\s\\d\\.\\-]+"),
141
    mShowInvalidLines( true ), mWkbType( QGis::WKBNoGeometry )
137 142
{
138 143
  // Get the file name and mDelimiter out of the uri
139 144
  mFileName = uri.left( uri.indexOf( "?" ) );
......
147 152
  mDelimiter = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : "";
148 153
  temp = parameters.filter( "delimiterType=" );
149 154
  mDelimiterType = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : "";
155
  temp = parameters.filter( "wktField=" );
156
  QString wktField = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : "";
150 157
  temp = parameters.filter( "xField=" );
151 158
  QString xField = temp.size() ? temp[0].mid( temp[0].indexOf( "=" ) + 1 ) : "";
152 159
  temp = parameters.filter( "yField=" );
......
157 164
  mFileName  = QUrl::fromPercentEncoding( mFileName.toUtf8() );
158 165
  mDelimiter = QUrl::fromPercentEncoding( mDelimiter.toUtf8() );
159 166
  mDelimiterType = QUrl::fromPercentEncoding( mDelimiterType.toUtf8() );
167
  wktField    = QUrl::fromPercentEncoding( wktField.toUtf8() );
160 168
  xField    = QUrl::fromPercentEncoding( xField.toUtf8() );
161 169
  yField    = QUrl::fromPercentEncoding( yField.toUtf8() );
170

  
171
  mHasWktField = wktField != "";
172
 
162 173
  skipLines = QUrl::fromPercentEncoding( skipLines.toUtf8() );
163 174

  
164 175
  mSkipLines = skipLines.toInt();
......
167 178
  QgsDebugMsg( "Delimited text file is: " + mFileName );
168 179
  QgsDebugMsg( "Delimiter is: " + mDelimiter );
169 180
  QgsDebugMsg( "Delimiter type is: " + mDelimiterType );
181
  QgsDebugMsg( "wktField is: " + xField );
170 182
  QgsDebugMsg( "xField is: " + xField );
171 183
  QgsDebugMsg( "yField is: " + yField );
172 184
  QgsDebugMsg( "skipLines is: " + QString::number( mSkipLines ) );
......
218 230
  QString line;
219 231
  mNumberFeatures = 0;
220 232
  int lineNumber = 0;
221
  bool firstPoint = true;
222 233
  bool hasFields = false;
223 234
  while ( !mStream->atEnd() )
224 235
  {
......
236 247
      // fields vector
237 248
      QStringList fieldList = splitLine( line );
238 249

  
250
	  mFieldCount = fieldList.count();
251

  
239 252
      // We don't know anything about a text based field other
240 253
      // than its name. All fields are assumed to be text
241 254
      int fieldPos = 0;
242
      for ( QStringList::Iterator it = fieldList.begin(); it != fieldList.end(); ++it )
243
      {
244
        QString field = *it;
255
	  for( int column = 0; column < mFieldCount; column++ )
256
	  {
257
        QString field = fieldList[column];
245 258
        if ( field.length() > 0 )
246 259
        {
247
          // for now, let's set field type as text
248
          attributeFields[fieldPos] = QgsField( *it, QVariant::String, "Text" );
249 260

  
250 261
          // check to see if this field matches either the x or y field
251
          if ( xField == *it )
262
          if ( wktField == field )
252 263
          {
253
            QgsDebugMsg( "Found x field: " + ( *it ) );
254
            mXFieldIndex = fieldPos;
264
            QgsDebugMsg( "Found wkt field: " + ( field ) );
265
            mWktFieldIndex = column;
255 266
          }
256
          else if ( yField == *it )
267
          else if ( xField == field )
257 268
          {
258
            QgsDebugMsg( "Found y field: " + ( *it ) );
259
            mYFieldIndex = fieldPos;
269
            QgsDebugMsg( "Found x field: " + ( field ) );
270
            mXFieldIndex = column;
260 271
          }
272
          else if ( yField == field )
273
          {
274
            QgsDebugMsg( "Found y field: " + ( field ) );
275
            mYFieldIndex = column;
276
          }
261 277

  
262
          QgsDebugMsg( "Adding field: " + ( *it ) );
278
		  // WKT geometry field won't be displayed in attribute tables
279
		  if( column == mWktFieldIndex ) continue;
280

  
281
          QgsDebugMsg( "Adding field: " + ( field ) );
263 282
          // assume that the field could be integer or double
283
          // for now, let's set field type as text
284
		  attributeColumns.append(column);
285
          attributeFields[fieldPos] = QgsField( field, QVariant::String, "Text" );
264 286
          couldBeInt.insert( fieldPos, true );
265 287
          couldBeDouble.insert( fieldPos, true );
266 288
          fieldPos++;
267 289
        }
268 290
      }
291
	  if( mWktFieldIndex >= 0 ) { mXFieldIndex = -1; mYFieldIndex=-1; }
269 292
      QgsDebugMsg( "Field count for the delimited text file is " + QString::number( attributeFields.size() ) );
270 293
      hasFields = true;
271 294
    }
272
    else //field names already read
295
    else // hasFields == true - field names already read
273 296
    {
274
      mNumberFeatures++;
275 297

  
276 298
      // split the line on the delimiter
277 299
      QStringList parts = splitLine( line );
278 300

  
279 301
      // Skip malformed lines silently. Report line number with nextFeature()
280
      if ( attributeFields.size() != parts.size() )
302
      if ( parts.size() != mFieldCount )
281 303
      {
282 304
        continue;
283 305
      }
284 306

  
285
      // Get the x and y values, first checking to make sure they
286
      // aren't null.
287
      QString sX, sY;
288
      if ( mXFieldIndex >= 0 && mYFieldIndex >= 0 )
289
      {
290
        sX = parts[mXFieldIndex];
291
        sY = parts[mYFieldIndex];
292
      }
307
	  if( mHasWktField && mWktFieldIndex >= 0 )
308
	  {
309
          // Get the wkt - confirm it is valid, get the type, and 
310
		  // if compatible with the rest of file, add to the extents
293 311

  
294
      bool xOk = true;
295
      bool yOk = true;
296
      double x = sX.toDouble( &xOk );
297
      double y = sY.toDouble( &yOk );
312
		  QString sWkt = parts[mWktFieldIndex];
313
		  QgsGeometry *geom = 0;
314
		  try
315
		  {
316
		    if( ! mWktHasZM && sWkt.indexOf(mWktZMRegexp) >= 0 ) mWktHasZM = true;
317
			if( mWktHasZM )
318
			{
319
				sWkt.remove(mWktZMRegexp).replace(mWktCrdRegexp,"\\1");
320
			}
321
			geom = QgsGeometry::fromWkt(sWkt);
322
		  }
323
		  catch(...)
324
		  {
325
			  geom = 0;
326
		  }
298 327

  
299
      if ( xOk && yOk )
300
      {
301
        if ( !firstPoint )
302
        {
303
          mExtent.combineExtentWith( x, y );
304
        }
305
        else
306
        {
307
          // Extent for the first point is just the first point
308
          mExtent.set( x, y, x, y );
309
          firstPoint = false;
310
        }
311
      }
328
		  if( geom )
329
		  {
330
			  QGis::WkbType type = geom->wkbType();
331
			  if( type != QGis::WKBNoGeometry )
332
			  {
333
				  if( mNumberFeatures == 0 )
334
				  {
335
					  mNumberFeatures++;
336
					  mWkbType = type;
337
					  mExtent = geom->boundingBox();
338
				  }
339
				  else if( type == mWkbType )
340
				  {
341
					  mNumberFeatures++;
342
					  QgsRectangle bbox( geom->boundingBox());
343
					  mExtent.combineExtentWith( &bbox );
344
				  }
345
			  }
346
			  delete geom;
347
		  }
348
	  }
312 349

  
313
      int i = 0;
314
      for ( QStringList::iterator it = parts.begin(); it != parts.end(); ++it, ++i )
315
      {
350
	  else if( ! mHasWktField && mXFieldIndex >= 0 && mYFieldIndex >= 0 )
351
	  {
352

  
353
          // Get the x and y values, first checking to make sure they
354
          // aren't null.
355

  
356
		  QString sX = parts[mXFieldIndex];
357
		  QString sY = parts[mYFieldIndex];
358

  
359
		  bool xOk = true;
360
		  bool yOk = true;
361
		  double x = sX.toDouble( &xOk );
362
		  double y = sY.toDouble( &yOk );
363

  
364
		  if ( xOk && yOk )
365
		  {
366
			if ( mNumberFeatures > 0 )
367
			{
368
			  mExtent.combineExtentWith( x, y );
369
			}
370
			else
371
			{
372
			  // Extent for the first point is just the first point
373
			  mExtent.set( x, y, x, y );
374
   			  mWkbType = QGis::WKBPoint;
375
			}
376
			mNumberFeatures++;
377
		  }
378
	  }
379

  
380
      for( int i = 0; i < attributeFields.size(); i++ )
381
	  {
382
		QString &value = parts[attributeColumns[i]];
383
		if( value.isEmpty()) continue;
316 384
        // try to convert attribute values to integer and double
317
        if ( couldBeInt[i] && !it->isEmpty() )
385
        if ( couldBeInt[i] )
318 386
        {
319
          it->toInt( &couldBeInt[i] );
387
          value.toInt( &couldBeInt[i] );
320 388
        }
321
        if ( couldBeDouble[i] && !it->isEmpty() )
389
        if ( couldBeDouble[i] )
322 390
        {
323
          it->toDouble( &couldBeDouble[i] );
391
          value.toDouble( &couldBeDouble[i] );
324 392
        }
325 393
      }
326 394
    }
327 395
  }
328 396

  
329
  if ( mXFieldIndex < 0 || mYFieldIndex < 0 )
330
  {
331
    mWkbType = QGis::WKBNoGeometry;
332
  }
333

  
334 397
  // now it's time to decide the types for the fields
335 398
  for ( QgsFieldMap::iterator it = attributeFields.begin(); it != attributeFields.end(); ++it )
336 399
  {
......
379 442
    // lex the tokens from the current data line
380 443
    QStringList tokens = splitLine( line );
381 444

  
382
    bool xOk = false;
383
    bool yOk = false;
384
    bool geometryOk = false;
445
	QgsGeometry *geom = 0;
385 446

  
386
    // Skip indexing malformed lines.
387
    if ( mXFieldIndex < 0 || mYFieldIndex < 0 )
388
    {
389
      geometryOk = false;
390
    }
391
    else if ( attributeFields.size() == tokens.size() )
392
    {
393
      x = tokens[mXFieldIndex].toDouble( &xOk );
394
      y = tokens[mYFieldIndex].toDouble( &yOk );
395
      geometryOk = ( xOk && yOk );
396
    }
447
    if( mHasWktField && mWktFieldIndex >= 0 )
448
	{
449
		try
450
		{
451
			QString &sWkt = tokens[mWktFieldIndex];
452
			if( mWktHasZM )
453
			{
454
				sWkt.remove(mWktZMRegexp).replace(mWktCrdRegexp,"\\1");
455
			}
397 456

  
398
    // Give every valid line in the file an id, even if it's not
399
    // in the current extent or bounds.
400
    ++mFid;             // increment to next feature ID
457
			geom = QgsGeometry::fromWkt(sWkt);
458
		}
459
		catch(...)
460
		{
461
			geom = 0;
462
		}
401 463

  
402
    // skip the feature if it's out of current bounds
403
    if ( ! boundsCheck( x, y ) )
404
      continue;
464
		if( geom && geom->wkbType() != mWkbType )
465
		{
466
			delete geom;
467
			geom = 0;
468
		}
469
		mFid++;
470
		if( ! boundsCheck(geom))
471
		{
472
			delete geom;
473
			geom = 0;
474
		}
475
	}
476
	else if( ! mHasWktField && mXFieldIndex >= 0 && mYFieldIndex >= 0 )
477
	{
478
		bool xOk, yOk;
479
		double x = tokens[mXFieldIndex].toDouble(&xOk);
480
		double y = tokens[mYFieldIndex].toDouble(&yOk);
481
		if( xOk && yOk )
482
		{
483
			mFid++;
484
			if( boundsCheck(x,y) )
485
			{
486
				geom = QgsGeometry::fromPoint(QgsPoint(x,y));
487
			}
488
		}
489
	}
405 490

  
406
    // at this point, one way or another, the current feature values
407
    // are valid
408
    feature.setValid( true );
491
	// If no valid geometry skip to the next line
409 492

  
410
    feature.setFeatureId( mFid );
493
	if( ! geom ) continue;
411 494

  
412
    QByteArray buffer;
413
    QDataStream s( &buffer, static_cast<QIODevice::OpenMode>( QIODevice::WriteOnly ) ); // open on buffers's data
495
    // At this point the current feature values are valid
414 496

  
415
    switch ( QgsApplication::endian() )
416
    {
417
      case QgsApplication::NDR :
418
        // we're on a little-endian platform, so tell the data
419
        // stream to use that
420
        s.setByteOrder( QDataStream::LittleEndian );
421
        s << ( quint8 )1; // 1 is for little-endian
422
        break;
423
      case QgsApplication::XDR :
424
        // don't change byte order since QDataStream is big endian by default
425
        s << ( quint8 )0; // 0 is for big-endian
426
        break;
427
      default :
428
        QgsDebugMsg( "unknown endian" );
429
        //delete [] geometry;
430
        return false;
431
    }
497
    feature.setValid( true );
432 498

  
433
    s << ( quint32 )QGis::WKBPoint;
434
    s << x;
435
    s << y;
499
    feature.setFeatureId( mFid );
436 500

  
437
    unsigned char* geometry = 0;
438
    if ( geometryOk )
439
    {
440
      geometry = new unsigned char[buffer.size()];
441
      memcpy( geometry, buffer.data(), buffer.size() );
442
      feature.setGeometryAndOwnership( geometry, sizeof( wkbPoint ) );
443
    }
444
    else
445
    {
446
      feature.setGeometryAndOwnership( 0, 0 );
447
    }
501
	feature.setGeometry( geom );
448 502

  
449 503
    for ( QgsAttributeList::const_iterator i = mAttributesToFetch.begin();
450 504
          i != mAttributesToFetch.end();
451 505
          ++i )
452 506
    {
507
	  QString &value = tokens[attributeColumns[*i]];
453 508
      QVariant val;
454 509
      switch ( attributeFields[*i].type() )
455 510
      {
456 511
        case QVariant::Int:
457
          if ( !tokens[*i].isEmpty() )
458
            val = QVariant( tokens[*i].toInt() );
512
          if ( !value.isEmpty() )
513
            val = QVariant( value );
459 514
          else
460 515
            val = QVariant( attributeFields[*i].type() );
461 516
          break;
462 517
        case QVariant::Double:
463
          if ( !tokens[*i].isEmpty() )
464
            val = QVariant( tokens[*i].toDouble() );
518
          if ( !value.isEmpty() )
519
            val = QVariant( value.toDouble() );
465 520
          else
466 521
            val = QVariant( attributeFields[*i].type() );
467 522
          break;
468 523
        default:
469
          val = QVariant( tokens[*i] );
524
          val = QVariant( value );
470 525
          break;
471 526
      }
472 527
      feature.addAttribute( *i, val );
......
589 644
  if ( mSelectionRectangle.isEmpty() || !mFetchGeom )
590 645
    return true;
591 646

  
592
  return ( x <= mSelectionRectangle.xMaximum() ) && ( x >= mSelectionRectangle.xMinimum() ) &&
593
         ( y <= mSelectionRectangle.yMaximum() ) && ( y >= mSelectionRectangle.yMinimum() );
647
  return mSelectionRectangle.contains( QgsPoint(x,y) );
594 648
}
649
/**
650
 * Check to see if the geometry is within the selection rectangle
651
 */
652
bool QgsDelimitedTextProvider::boundsCheck( QgsGeometry *geom )
653
{
654
  // no selection rectangle or geometry => always in the bounds
655
  if ( mSelectionRectangle.isEmpty() || !mFetchGeom )
656
    return true;
595 657

  
658
  return geom->boundingBox().intersects( mSelectionRectangle );
659
}
660

  
596 661
int QgsDelimitedTextProvider::capabilities() const
597 662
{
598 663
  return NoCapabilities;
......
606 671
}
607 672

  
608 673

  
609

  
610

  
611 674
QString  QgsDelimitedTextProvider::name() const
612 675
{
613 676
  return TEXT_PROVIDER_KEY;
src/providers/delimitedtext/qgsdelimitedtextprovider.h (working copy)
166 166
    bool boundsCheck( double x, double y );
167 167

  
168 168

  
169
    /**
170
     * Check to see if a geometry overlaps the selection
171
     * rectangle
172
     * @param geom geometry to test against bounds
173
     * @param y Y value of point
174
     * @return True if point is within the rectangle
175
    */
176
    bool boundsCheck( QgsGeometry *geom );
169 177

  
170

  
171

  
172 178
  private:
173 179

  
174 180
    //! Fields
181
    QList<int> attributeColumns;
175 182
    QgsFieldMap attributeFields;
176 183

  
177 184
    QgsAttributeList mAttributesToFetch;
......
181 188
    QRegExp mDelimiterRegexp;
182 189
    QString mDelimiterType;
183 190

  
191
	bool mHasWktField;
192
	int mFieldCount;  // Note: this includes field count for wkt field
184 193
    int mXFieldIndex;
185 194
    int mYFieldIndex;
195
	int mWktFieldIndex;
186 196

  
197
	// Handling of WKT types with .. Z, .. M, and .. ZM geometries (ie
198
	// Z values and/or measures).  mWktZMRegexp is used to test for and
199
	// remove the Z or M fields, and mWktCrdRegexp is used to remove the
200
	// extra coordinate values.
201

  
202
	bool mWktHasZM;
203
	QRegExp mWktZMRegexp;
204
	QRegExp mWktCrdRegexp;
205

  
187 206
    //! Layer extent
188 207
    QgsRectangle mExtent;
189 208

  
......
220 239
    };
221 240
    wkbPoint mWKBpt;
222 241

  
223
    QGis::WkbType mWkbType; //can be WKBPoint or NoGeometry
242
    QGis::WkbType mWkbType;
224 243

  
225 244
    QString readLine( QTextStream *stream );
226 245
    QStringList splitLine( QString line );