xdata 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ba86c934b70a2ddd1ce5539dfb75d68548fd8d1
4
+ data.tar.gz: 98d99acb80bbf8fb4bbb74f3a0346c08d8d34af4
5
+ SHA512:
6
+ metadata.gz: f68dc5b2251c8b69319a8f057b78e5e37d44c5774f733d1c360ee92da76c77f844c81c55b50efd3344f6817dd11a82c2f0c8a920714888f233f27e4efbe1cea5
7
+ data.tar.gz: 477ceef5ad94985a221e1e4f56f06651494214e63153c6569bbc24cc5640c2eeb6a5bb0561be08e51dd9458ccbe50da2255ff473bc730261b590192b4418a91c
@@ -0,0 +1,54 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
51
+
52
+
53
+ d.sh
54
+ .DS_Store
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in citysdk.gemspec
4
+ gemspec
5
+
6
+ gem 'georuby'
7
+ gem 'rgeo'
8
+ gem 'rgeo-geojson'
9
+ gem 'charlock_holmes'
10
+ gem 'feedjira'
@@ -0,0 +1,53 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ xdata (0.1)
5
+ charlock_holmes (~> 0.6)
6
+ curses (~> 1.0)
7
+ dbf (~> 2.0)
8
+ georuby (~> 2.0)
9
+ i18n (~> 0.7)
10
+ rgeo (~> 0.5)
11
+
12
+ GEM
13
+ remote: https://rubygems.org/
14
+ specs:
15
+ charlock_holmes (0.7.3)
16
+ curses (1.0.2)
17
+ dbf (2.0.13)
18
+ fastercsv (~> 1.5)
19
+ faraday (0.9.2)
20
+ multipart-post (>= 1.2, < 3)
21
+ faraday_middleware (0.10.0)
22
+ faraday (>= 0.7.4, < 0.10)
23
+ fastercsv (1.5.5)
24
+ feedjira (2.0.0)
25
+ faraday (~> 0.9)
26
+ faraday_middleware (~> 0.9)
27
+ loofah (~> 2.0)
28
+ sax-machine (~> 1.0)
29
+ georuby (2.5.2)
30
+ i18n (0.7.0)
31
+ loofah (2.0.3)
32
+ nokogiri (>= 1.5.9)
33
+ mini_portile2 (2.1.0)
34
+ multipart-post (2.0.0)
35
+ nokogiri (1.6.8)
36
+ mini_portile2 (~> 2.1.0)
37
+ pkg-config (~> 1.1.7)
38
+ pkg-config (1.1.7)
39
+ rgeo (0.5.3)
40
+ sax-machine (1.3.2)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ charlock_holmes
47
+ feedjira
48
+ georuby
49
+ rgeo
50
+ xdata!
51
+
52
+ BUNDLED WITH
53
+ 1.11.2
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Waag Society (http://waag.org)
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,32 @@
1
+ # XData rubyGEM
2
+
3
+ XData provides manipulation and meta-data extraction functionality for data files.
4
+ Typical context is Open Data and research data sets.
5
+
6
+ ## Installation
7
+
8
+ To install it just install the gem:
9
+
10
+ gem install xdata
11
+
12
+ This installs the command-line tool 'xdata'
13
+
14
+ ## Usage
15
+
16
+ xdata supports (zipped) shape files, geojson, csv and unformatted json files.
17
+
18
+ Type 'xdata --help' for usage information.
19
+
20
+ ## Dependencies
21
+
22
+ GEOS 3.2
23
+ proj 4.7
24
+
25
+
26
+ ## Contributing
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
@@ -0,0 +1,3 @@
1
+ require 'bundler'
2
+ require "bundler/gem_tasks"
3
+ Bundler::GemHelper.install_tasks
@@ -0,0 +1,696 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "yaml"
4
+ require "curses"
5
+ require "getoptlong"
6
+ require 'rgeo/geo_json'
7
+ include Curses
8
+ require_relative '../lib/xdata.rb'
9
+ include XData
10
+
11
+ include GeoRuby::SimpleFeatures
12
+
13
+ # $wkt = RGeo::WKRep::WKTGenerator.new
14
+
15
+ $outheight = 0
16
+ $outwidth = 0
17
+
18
+ ############# convenience ####################################################
19
+
20
+ class Array
21
+
22
+ def sharedStart
23
+ a = self.sort
24
+ w1 = a[0]
25
+ w2 = a[-1]
26
+ l = w1.length
27
+ i = 0
28
+ while(i < l and w1[i] == w2[i])
29
+ i += 1
30
+ end
31
+ w1[0...i]
32
+ end
33
+
34
+ def clip n=1
35
+ take size - n
36
+ end
37
+ end
38
+
39
+
40
+ class GeoRuby::SimpleFeatures::Point
41
+ def bounding_box
42
+ return [self.class.from_x_y_z(x, y, z), self.class.from_x_y_z(x, y, z)] if with_z
43
+ [self.class.from_x_y(x, y), self.class.from_x_y(x, y)]
44
+ end
45
+ end
46
+
47
+ ############# menus ##########################################################
48
+
49
+ class MenuItem
50
+ attr_accessor :t, :c, :k
51
+ def initialize(key,text,command,*args)
52
+ @k = key
53
+ @t = text
54
+ @c = command
55
+ @a = args.flatten
56
+ end
57
+ def call
58
+ (@a.length > 0) ? @c.call(@a) : @c.call
59
+ end
60
+ end
61
+
62
+ $currentMenu = nil
63
+
64
+ class Menu
65
+ def initialize(items)
66
+ @items = items
67
+ end
68
+
69
+ def replace(n,i)
70
+ @items[n]=i
71
+ end
72
+
73
+ def run(outwinproc = nil)
74
+ $currentMenu = self
75
+ $vpos = 3
76
+ banner
77
+ @items.each do |i|
78
+ setpos($vpos,1)
79
+ addstr(" #{i.k}: #{i.t}")
80
+ $vpos+=1
81
+ end
82
+ $vpos+=1
83
+ setpos($vpos,1)
84
+ addstr("please select")
85
+ setpos($vpos+1,0)
86
+ addstr("-" * Curses.cols)
87
+
88
+ $outwin.close if $outwin
89
+ $outheight = Curses.lines - $vpos - 3
90
+ $outwidth = Curses.cols
91
+ $outwin=Curses::Window.new( $outheight, $outwidth, $vpos+2, 1 )
92
+ $outwin.refresh
93
+ outwinproc.call if outwinproc
94
+
95
+ curs_set(0)
96
+ while true
97
+ noecho
98
+ c = getch
99
+ @items.each do |i|
100
+ if i.k == c
101
+ sherror('')
102
+ $outwin.clear
103
+ i.call
104
+ setpos($vpos,1)
105
+ clrtoeol
106
+ addstr("please select")
107
+ refresh
108
+ break
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+
116
+ ############# files ##########################################################
117
+
118
+ def doExit
119
+ saveConfig
120
+ close_screen
121
+ exit!(0)
122
+ end
123
+
124
+ def loadConfig
125
+ f = $file_hash[:file_path].gsub(/\.\w+$/,'.cfg')
126
+ if $file_hash[:file_path] =~ /^http(s)?:\/\/.+/
127
+ f = File.expand_path('~') + '/.xdata/' + f.split('/')[-1]
128
+ end
129
+ if File.exists?(f)
130
+ naf = {}
131
+ $file_hash = JSON.parse(File.read(f), symbolize_names: true)
132
+ $file_hash[:fields].map! { |f| (f.to_sym rescue f) || f }
133
+ $file_hash[:alternate_fields].each { |k,v| naf[(k.to_sym rescue k) || k] = v }
134
+ $file_hash[:alternate_fields] = naf
135
+ end
136
+ end
137
+
138
+
139
+ def saveConfig
140
+ return unless $file_reader
141
+ f = ''
142
+ f = $file_hash[:file_path].gsub(/\.\w+$/,'.cfg')
143
+ if $file_hash[:file_path] =~ /^http(s)?:\/\/.+/
144
+ f = File.expand_path('~') + '/.xdata/' + f.split('/')[-1]
145
+ end
146
+ begin
147
+ File.open(f,"w") do |fd|
148
+ fh = $file_hash.deep_copy
149
+ fd.write(JSON.pretty_generate(fh))
150
+ outMessage("Config saved at #{f}.")
151
+ end
152
+ rescue
153
+ outMessage("Failed to save config at #{f}.")
154
+ end
155
+ end
156
+
157
+
158
+ def saveFileCSV(path=nil)
159
+ return unless $file_reader
160
+ if path.blank?
161
+ r = {}
162
+ return cancelled unless get_input("Please enter file name",:fn,r,1)
163
+ path = r[:fn]
164
+ end
165
+ f = File.expand_path(path)
166
+
167
+ headers = $file_hash[:alternate_fields].values.compact
168
+ headers << 'geom' if $file_hash[:hasgeometry]
169
+
170
+ CSV.open(f, 'w') do |csv_object|
171
+ # write header line
172
+ csv_object << headers
173
+
174
+ $file_reader.content.each do |o|
175
+ a = []
176
+ o[:properties][:data].each do |k,v|
177
+ k2 = $file_hash[:alternate_fields][k]
178
+ a << v if !k2.blank?
179
+ end
180
+ if $file_hash[:hasgeometry]
181
+ geom = RGeo::GeoJSON.decode(o[:geometry].to_json, {:json_parser => :json})
182
+ a << geom.as_text
183
+ end
184
+ csv_object << a
185
+ end
186
+ end
187
+ outMessage("CSV file written: #{f}.")
188
+ end
189
+
190
+ def saveFileGJ(path=nil)
191
+ return unless $file_reader
192
+ if path.blank?
193
+ r = {}
194
+ return cancelled unless get_input("Please enter file name",:fn,r,1)
195
+ path = r[:fn]
196
+ end
197
+ f = File.expand_path(path)
198
+ File.open(f,"w") do |fd|
199
+ fd.write('{"type": "FeatureCollection", "crs": {"type": "EPSG", "properties": { "code": "' + $file_hash[:srid].to_s + '" } }, "features": [')
200
+ started = false
201
+ $file_reader.content.each do |o|
202
+ fd.write(started ? ",\n" : "\n")
203
+ started = true
204
+ a = formatObject(o)
205
+ a["type"] = "Feature"
206
+ fd.write(a.to_json)
207
+ end
208
+ fd.write("\n] }\n")
209
+ end
210
+ outMessage("JSON file written: #{f}.")
211
+ end
212
+
213
+
214
+ def formatObject(o)
215
+ begin
216
+ tp = {}
217
+ # log JSON.pretty_generate($file_hash)
218
+ o[:properties][:data].each do |k,v|
219
+ k2 = $file_hash[:alternate_fields][k]
220
+ tp[k2] = v if !k2.blank?
221
+ end
222
+ o[:properties] = tp
223
+ rescue => e
224
+ sherror("Error: #{e.message}")
225
+ end
226
+ o
227
+ end
228
+
229
+ def showSample
230
+ return unless $file_reader
231
+ begin
232
+ o = $file_reader.content[rand($file_reader.content.length)].deep_copy
233
+ # log JSON.pretty_generate(o)
234
+ if o[:geometry] and o[:geometry].class == Hash
235
+ o[:geometry][:coordinates] = ['...'] if o[:geometry][:coordinates] and o[:geometry][:type] != 'Point'
236
+ else
237
+ o[:geometry] = nil
238
+ end
239
+ $outwin.clear
240
+ $outwin.setpos(1,0)
241
+ $outwin.addstr formatObject(o).to_yaml
242
+ $outwin.refresh
243
+ rescue => e
244
+ sherror("Error: #{e.message}")
245
+ end
246
+ end
247
+
248
+
249
+ def fileSummary(vp)
250
+
251
+ $outwin.clear
252
+
253
+ $outwin.setpos(vp+=1,0)
254
+ $outwin.addstr sprintf("%14s #{$file_hash[:file_path]}", "file:")
255
+
256
+ $outwin.setpos(vp+=1,0)
257
+ $outwin.addstr sprintf("%14s #{$file_hash[:rowcount]}", "total rows:")
258
+
259
+ $outwin.setpos(vp+=1,0)
260
+ $outwin.addstr sprintf("%14s #{$file_hash[:unique_id].to_s}","unique id:")
261
+
262
+ if $file_hash[:postcode]
263
+ $outwin.setpos(vp+=1,0)
264
+ $outwin.addstr sprintf("%14s #{$file_hash[:postcode]}","postcode in:")
265
+ end
266
+
267
+ if $file_hash[:housenumber]
268
+ $outwin.setpos(vp+=1,0)
269
+ $outwin.addstr sprintf("%14s #{$file_hash[:housenumber]}","address/number in:")
270
+ end
271
+
272
+ if $file_hash[:hasgeometry]
273
+ $outwin.setpos(vp+=1,0)
274
+ $outwin.addstr sprintf("%14s found in %s; srid: #{$file_hash[:srid]}","geometry:",$file_hash[:hasgeometry])
275
+ end
276
+
277
+ $outwin.refresh
278
+ end
279
+
280
+ def loadFile(filePath=nil)
281
+ $file_reader = nil
282
+
283
+ if filePath.nil?
284
+ return cancelled unless get_input("file path", :file_path, $file_hash ,1)
285
+ else
286
+ $file_hash[:file_path] = filePath
287
+ end
288
+
289
+ begin
290
+
291
+ $file_hash[:keep_geom] = false
292
+ $file_reader = FileReader.new($file_hash)
293
+ loadConfig
294
+
295
+ if !$file_hash[:fields].blank? and $file_hash[:fields][$file_hash[:fields].length-1] == nil
296
+ $file_hash[:fields] = $file_hash[:fields].clip
297
+ end
298
+
299
+ if $outwin
300
+ fileSummary(1)
301
+ end
302
+ rescue => e
303
+ sherror("Error reading file: #{e.inspect}")
304
+ end
305
+
306
+ end
307
+
308
+ def editFields
309
+ props = $file_reader.content[rand($file_reader.content.length)][:properties][:data]
310
+ $outwin.clear
311
+ outAddstr(1, "For each field, please choose Accept, Rename or Ignore")
312
+ vp = 3
313
+ accepted_fields = []
314
+ accepted_fields << $file_hash[:unique_id]
315
+ curs_set(2)
316
+ $file_hash[:fields].each do |f|
317
+ outAddstr(vp,sprintf("-- sample: #{props[f]} --"))
318
+ field = getFieldHeader(vp+2,0,sprintf("Field: '#{f}'; (a/r/i) "),f)
319
+ return cancelled if field == 27
320
+ $file_hash[:alternate_fields][f] = field
321
+ end
322
+ curs_set(0)
323
+ $file_reader.guess_name
324
+ $file_reader.get_address
325
+ $outwin.clear
326
+ fileSummary(1)
327
+ $outwin.refresh
328
+ end
329
+
330
+
331
+ ############# utils ##########################################################
332
+
333
+
334
+ def assignGeometry
335
+ list = $file_hash[:fields].map {|f| $file_hash[:alternate_fields][f].to_s}
336
+ $outwin.clear
337
+ x = get_input_from_list('please select x (longitude) field: ', list , 3)
338
+ y = get_input_from_list(' select y (latitude) field: ', list , 3)
339
+ unless (x.blank? and y.blank?)
340
+ $file_reader.find_geometry(x.to_sym, y.to_sym)
341
+ $file_reader.guess_srid
342
+ $file_reader.findExtends
343
+ end
344
+ $outwin.clear
345
+ fileSummary(1)
346
+ $outwin.refresh
347
+ end
348
+
349
+ def getFieldHeader(v,h,s,f)
350
+ a = ''
351
+ $outwin.setpos(v,h)
352
+ $outwin.clrtoeol
353
+ $outwin.addstr s
354
+ $outwin.refresh
355
+
356
+ a = charIn(["A","a","R","r","I","i"])
357
+ return 27 if a.ord == 27
358
+
359
+ case a.downcase
360
+ when 'a'
361
+ return f
362
+ when 'i'
363
+ return nil
364
+ when 'r'
365
+ $outwin.setpos(v,h)
366
+ $outwin.clrtoeol
367
+ $outwin.addstr "Replacement name for field '#{f}': "
368
+ $outwin.refresh
369
+ echo
370
+ s = $outwin.getstr
371
+ noecho
372
+ s
373
+ end
374
+ end
375
+
376
+
377
+ def isTrue?(o)
378
+ return true if
379
+ (o == true) or
380
+ (o =~ /^y$/i) or
381
+ (o =~ /^t$/i) or
382
+ (o =~ /^true$/i) or
383
+ (o =~ /^yes$/i) or
384
+ (o =~ /^j$/i) or
385
+ (o =~ /^ja$/i)
386
+ false
387
+ end
388
+
389
+ def cancelled
390
+ outMessage("Cancelled...")
391
+ end
392
+
393
+ def showMatches(l,v)
394
+ $outwin.clear
395
+ l.each do |i|
396
+ $outwin.setpos(v+=1,0)
397
+ $outwin.addstr("- #{i}")
398
+ end
399
+ $outwin.refresh
400
+ end
401
+
402
+ def get_input_from_list(prompt, list, vp, res = '')
403
+ # 259 up
404
+ # 258 dn
405
+ udindex = -1
406
+ matchl = list
407
+ prv = inp = nil
408
+ curs_set(1)
409
+ $outwin.keypad(true)
410
+ while true
411
+ $outwin.setpos(vp,0)
412
+ $outwin.clrtoeol
413
+ $outwin.addstr("#{prompt} -> #{res}")
414
+ $outwin.refresh
415
+ prv = inp
416
+ inp = $outwin.getch
417
+
418
+ case inp.ord
419
+
420
+ when 259 # up
421
+ if udindex == -1
422
+ matchl = list.select { |i| i.starts_with?(res) }
423
+ udindex = 0
424
+ end
425
+ res = matchl[udindex -= 1] if (udindex > 0)
426
+ when 258 # down
427
+ if udindex == -1
428
+ matchl = list.select { |i| i.starts_with?(res) }
429
+ udindex = 0
430
+ end
431
+ res = matchl[udindex += 1] if (udindex < matchl.length-1)
432
+ when 27
433
+ outMessage("Cancelled")
434
+ $outwin.keypad(false)
435
+ return ''
436
+ when 9
437
+ if prv.ord == 9
438
+ showMatches(matchl,vp+1)
439
+ else
440
+ matchl = list.select { |i| i.starts_with?(res)}
441
+ res = matchl.sharedStart if matchl[0]
442
+ udindex = -1
443
+ end
444
+ when 10,13
445
+ break
446
+ when 127,8
447
+ udindex = -1
448
+ res = res[0...-1]
449
+ else
450
+ udindex = -1
451
+ res << inp if (inp.ord > 31)
452
+ end
453
+ end
454
+ $outwin.keypad(false)
455
+ curs_set(0)
456
+ res
457
+ end
458
+
459
+
460
+ def self.get_string_array(prompt,vp)
461
+ ret = []
462
+ echo
463
+ tempwin=$outwin.subwin( $outheight - vp - 2, $outwidth-2, $vpos+vp+2, 2 )
464
+ tempwin.setpos(1,1)
465
+ tempwin.addstr(prompt)
466
+ vp = 2
467
+ tempwin.setpos(vp,1)
468
+ loop do
469
+ tempwin.setpos(vp,1)
470
+ tempwin.addstr("-> ")
471
+ tempwin.refresh
472
+ s = tempwin.getstr
473
+ break if s.length == 0
474
+ vp += 1
475
+ ret << s
476
+ end
477
+ noecho
478
+ tempwin.clear
479
+ tempwin.close
480
+ $outwin.refresh
481
+ ret
482
+ end
483
+
484
+
485
+ def get_input(prompt, symbol, hash, vp, defs=nil)
486
+ res = defs ? defs : ''
487
+ echo
488
+ curs_set(1)
489
+ $outwin.keypad(true)
490
+
491
+ while true
492
+ if hash[symbol] and symbol != :password
493
+ outAddstr(vp,"#{prompt} (#{hash[symbol]}) -> #{res}")
494
+ else
495
+ outAddstr(vp,"#{prompt} -> #{res}")
496
+ end
497
+ $outwin.refresh
498
+ inp = $outwin.getch
499
+ case inp.ord
500
+ when 27
501
+ outMessage("Cancelled")
502
+ return false
503
+ when 10,13
504
+ break
505
+ when 127,8
506
+ res = res[0...-1]
507
+ else
508
+ res << inp if (inp.ord > 31)
509
+ end
510
+ end
511
+
512
+ curs_set(0)
513
+ noecho
514
+ hash[symbol] = res.strip if res.length > 0
515
+ $outwin.keypad(false)
516
+ true
517
+ end
518
+
519
+
520
+
521
+ def banner
522
+ clear
523
+ setpos(0,0)
524
+ addstr "XData interactive; file is: #{$file_hash[:file_path]}"
525
+ setpos(1,0)
526
+ end
527
+
528
+ def runFromTop
529
+ $fileMenu.run
530
+ end
531
+
532
+ def yesNo?(p,v=nil)
533
+ $outwin.setpos(v,0) if v
534
+ $outwin.addstr("#{p} ")
535
+ $outwin.refresh
536
+ ['Y','y','j','J'].include? $outwin.getch
537
+ end
538
+
539
+
540
+
541
+ def outAddstr(v,s)
542
+ $outwin.setpos(v,0)
543
+ $outwin.clrtoeol
544
+ $outwin.addstr(s)
545
+ $outwin.refresh
546
+ end
547
+
548
+
549
+ def sherror(e)
550
+ if $outwin
551
+ setpos(Curses.lines-1,1)
552
+ clrtoeol
553
+ addstr($error=e)
554
+ refresh
555
+ else
556
+ jsonlog({:exept => e})
557
+ end
558
+ false
559
+ end
560
+
561
+ def log(m)
562
+ File.open(File.expand_path('~/.xdata/xdata.log'), "a") do |f|
563
+ f.write("#{m}\n")
564
+ end
565
+ end
566
+
567
+
568
+ def not_yet
569
+ outMessage("Not yet implemented")
570
+ end
571
+
572
+ def charIn(arr)
573
+ a = $outwin.getch
574
+ while !(arr.include?(a) or a.ord == 27)
575
+ a = $outwin.getch
576
+ end
577
+ a
578
+ end
579
+
580
+ def outObject(o,v,h)
581
+ arr = o.to_yaml.split("\n")
582
+ arr.each do |a|
583
+ $outwin.setpos(v+=1,h)
584
+ $outwin.addstr a.strip
585
+ end
586
+ end
587
+
588
+ def outMessage(m)
589
+ if $outwin
590
+ $outwin.clear
591
+ $outwin.setpos(1,0)
592
+ $outwin.addstr m
593
+ $outwin.refresh
594
+ else
595
+ STDERR.puts m
596
+ end
597
+ end
598
+
599
+
600
+
601
+
602
+ ############# run!! ##########################################################
603
+
604
+
605
+
606
+ $outwin = nil
607
+ $error = nil
608
+ $file_hash = {}
609
+
610
+
611
+ $fileMenu = Menu.new( [
612
+ MenuItem.new('1','load & analyse', lambda{loadFile}),
613
+ MenuItem.new('2','rename fields', lambda{editFields}),
614
+ MenuItem.new('3','show sample', lambda{showSample}),
615
+ MenuItem.new('4','assign geom columns', lambda { assignGeometry }),
616
+ MenuItem.new('s','file summary', lambda { fileSummary(1) }),
617
+ MenuItem.new('g','save (geo)json', lambda { saveFileGJ }),
618
+ MenuItem.new('c','save csv', lambda { saveFileCSV }),
619
+ MenuItem.new('q','exit', lambda { doExit } )
620
+ ])
621
+
622
+ trap(:INT) do
623
+ doExit
624
+ end
625
+
626
+
627
+ def printHelp
628
+ STDERR.puts 'HELP!'
629
+ doExit
630
+ end
631
+
632
+ opts = GetoptLong.new(
633
+ [ '--help', '-h', GetoptLong::NO_ARGUMENT ],
634
+ [ '--bounds', '-b', GetoptLong::NO_ARGUMENT ],
635
+ [ '--summary', '-s', GetoptLong::NO_ARGUMENT ],
636
+ [ '--csv', '-c', GetoptLong::OPTIONAL_ARGUMENT ],
637
+ [ '--geojson', '-g', GetoptLong::OPTIONAL_ARGUMENT ]
638
+ )
639
+
640
+
641
+ $command = []
642
+ $csvOut = nil
643
+ $geoOut = nil
644
+
645
+
646
+ opts.each do |opt, arg|
647
+ case opt
648
+ when '--help'
649
+ printHelp()
650
+ when '--bounds'
651
+ $command << :bounds
652
+ when '--summary'
653
+ $command << :summary
654
+ when '--csv'
655
+ if arg == ''
656
+ $csvOut = ''
657
+ else
658
+ $csvOut = arg
659
+ end
660
+ when '--geojson'
661
+ if arg == ''
662
+ $geoOut = ''
663
+ else
664
+ $geoOut = arg
665
+ end
666
+ end
667
+ end
668
+
669
+ FileUtils.mkdir_p(File.expand_path('~') + '/' + ".xdata")
670
+
671
+
672
+ loadFile(ARGV[0]) if (ARGV.length == 1)
673
+
674
+
675
+ if $command.blank?
676
+ init_screen
677
+ nl
678
+ noecho
679
+ runFromTop
680
+ else
681
+ case $command[0]
682
+ when :summary
683
+ puts JSON.pretty_generate($file_hash)
684
+ exit(0)
685
+ when :bounds
686
+ if $file_hash[:bounds]
687
+ puts JSON.pretty_generate($file_hash[:bounds])
688
+ exit(0)
689
+ else
690
+ STDERR.puts "No geography or postcodes found in file."
691
+ end
692
+ else
693
+ exit(-1)
694
+ end
695
+ end
696
+