xdata 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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
+