natour 0.3.0 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 653a7f10629f9d5cb63e991eba1e4693e88dcf3b26d0a92978f98601d8223507
4
- data.tar.gz: 8e8671274bab8ac94bec16bcd5d967cdd337ccfb81e63b406d3d066d0689951d
3
+ metadata.gz: 6c649407352883c9e93ab6d6470413127d54803bb35dc72beb83292c9e521626
4
+ data.tar.gz: dbeb8f8ae4a412d7832c07ab66d717d40bb5e2ecd096fb076655ef65a6840b53
5
5
  SHA512:
6
- metadata.gz: f8a3ec14f64440b08bc1e291ac20bd7cff86cef27509010c253779eab32ca26098667b8d5b13b216f8f486174b326f201941201da98a4b4ed8e22d3a43d2407f
7
- data.tar.gz: 3554afc251f92900f32fe59e925c690cc9f1de77f7e3b84c0a94c5885a2706543ddca2b0a6cd50c11a0ea13918923c3e1766ef7059d938ed89fae49d49135a8c
6
+ metadata.gz: b5a810b2ea0220c9eded025e0f00f8e4f782a42b54128f71a2929a84a535e75f45bb3ddff0cf72f7dad08e047c1aaef3b88800e778901405357efd11336b5db0
7
+ data.tar.gz: a928d426c1840cb57fb7ab58064850572fc2a7e14aeda6086b2c9560c8b2d7e3e8253c268a9070bef91e4c7fb2c0f26daf2e5664a043f29b595d192a0299137f
data/CHANGELOG.adoc CHANGED
@@ -6,6 +6,57 @@ The format is based on https://keepachangelog.com/en/1.0.0/[Keep a Changelog^],
6
6
 
7
7
  == Unreleased
8
8
 
9
+ == 0.7.0 - 2022-02-14
10
+
11
+ === Added
12
+
13
+ - Add support for Ruby 3.0 and 3.1
14
+ - Support species lists from Vogelführer Birdlife Schweiz
15
+
16
+ === Changed
17
+
18
+ - Return the filenames from `create` and `convert`
19
+
20
+ === Removed
21
+
22
+ - Drop support for Ruby 2.5
23
+
24
+ === Fixed
25
+
26
+ - Add revision date to the AsciiDoc output regardless of the author
27
+ - Prevent conversion from being aborted if a built-in date/time attribute is missing
28
+
29
+ == 0.6.0 - 2021-11-20
30
+
31
+ === Fixed
32
+
33
+ - Support GPX files containing only mandatory elements (i.e. no extensions)
34
+ - Consider also the image dimensions to determine the orientation, even if the tag is present
35
+
36
+ == 0.5.0 - 2021-08-16
37
+
38
+ === Added
39
+
40
+ - Reflect the date of the last update in the revision date
41
+ - Add captions to species lists in the AsciiDoc output
42
+ - Group species lists by taxonomic groups in the AsciiDoc output
43
+
44
+ === Fixed
45
+
46
+ - Support species lists of Flora Helvetica exported from Favoriten
47
+ - Consider botanical names with the authority name preceding the subspecies
48
+
49
+ == 0.4.0 - 2021-05-02
50
+
51
+ === Added
52
+
53
+ - Show date/time of images in draft output
54
+ - Support preferred conversion backend for draft output
55
+
56
+ === Changed
57
+
58
+ - Remove redundant method `Image.portrait?`
59
+
9
60
  == 0.3.0 - 2021-03-07
10
61
 
11
62
  === Changed
data/README.adoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = natour
2
2
 
3
- This https://rubygems.org/[RubyGem^] provides an application and a library for reports on nature activities.
3
+ This https://rubygems.org/gems/natour[RubyGem^] provides an application and a library for reports on nature activities.
4
4
 
5
5
  == Installation
6
6
 
data/bin/natour CHANGED
@@ -17,6 +17,7 @@ config = Natour::Config.load_file(
17
17
  'adoc-author' => nil,
18
18
  'backend' => 'pdf',
19
19
  'draft' => false,
20
+ 'draft-backend' => nil,
20
21
  'image-maxdim' => 1800
21
22
  }
22
23
  )
@@ -27,10 +28,11 @@ option_parser = OptionParser.new do |opts|
27
28
  opts.separator(WordWrap.ww(
28
29
  'If PATH refers to a directory, one or more reports are created in ' \
29
30
  'AsciiDoc format. GPS tracks, images and species lists (Kosmos ' \
30
- 'Vogelführer, ornitho.ch, Flora Helvetica, Info Flora Online-Feldbuch) ' \
31
- 'are included. Images of the map section covered by the GPS tracks are ' \
32
- 'also created and included. If PATH refers to an AsciiDoc file, it is ' \
33
- 'converted to PDF (or any other supported format).'
31
+ 'Vogelführer, Vogelführer Birdlife Schweiz, ornitho.ch, Flora Helvetica,' \
32
+ ' Info Flora Online-Feldbuch) are included. Images of the map section ' \
33
+ 'covered by the GPS tracks are also created and included. If PATH refers' \
34
+ ' to an AsciiDoc file, it is converted to PDF (or any other supported ' \
35
+ 'format).'
34
36
  ))
35
37
  opts.separator('')
36
38
  opts.separator('General:')
@@ -69,6 +71,9 @@ option_parser = OptionParser.new do |opts|
69
71
  opts.on('--[no-]draft', 'Show additional information (e.g. image paths)') do |value|
70
72
  config['draft'] = value
71
73
  end
74
+ opts.on('--draft-backend BACKEND', 'Preferred conversion backend for draft (pdf, html5 ...)') do |value|
75
+ config['draft-backend'] = value
76
+ end
72
77
  opts.on('--image-maxdim DIM', 'Shrink oversized images (PDF only)') do |value|
73
78
  config['image-maxdim'] = value.to_i
74
79
  end
@@ -106,6 +111,7 @@ else
106
111
  overwrite: config['overwrite'],
107
112
  backend: config['backend'],
108
113
  draft: config['draft'],
114
+ draft_backend: config['draft-backend'],
109
115
  image_maxdim: config['image-maxdim']
110
116
  )
111
117
  end
@@ -28,11 +28,10 @@ module Natour
28
28
 
29
29
  doc = []
30
30
  doc << "= #{title}"
31
- if author
32
- doc << author
33
- doc << Date.today.strftime('%d.%m.%Y')
34
- end
31
+ doc << author if author
32
+ doc << ':revdate: {docdate}'
35
33
  doc << ':figure-caption!:'
34
+ doc << ':table-caption!:'
36
35
  doc << ':pdf-page-mode: none'
37
36
  doc << ':title-page:'
38
37
  if title_image
@@ -77,10 +76,10 @@ module Natour
77
76
  doc << '== Bilder'
78
77
  doc << ''
79
78
  images.each do |image|
80
- width = if image.portrait?
81
- '40%'
82
- else
79
+ width = if image.landscape?
83
80
  '80%'
81
+ else
82
+ '40%'
84
83
  end
85
84
  doc << '.Abbildung {counter:image}'
86
85
  doc << "image::#{Pathname(doc_root).join(image.path)}[width=#{width}]"
@@ -88,35 +87,45 @@ module Natour
88
87
  end
89
88
  end
90
89
  unless species_lists.empty?
91
- birds_info = OpenStruct.new(
92
- title: 'Vogelarten',
93
- headers: %w[Deutscher\ Name Wissenschaftlicher\ Name],
94
- columns: %i[name_de name]
95
- )
96
- plants_info = OpenStruct.new(
97
- title: 'Pflanzenarten',
98
- headers: %w[Wissenschaftlicher\ Name Deutscher\ Name],
99
- columns: %i[name name_de]
100
- )
101
90
  doc << '<<<'
102
91
  doc << ''
103
92
  doc << '== Artenlisten'
104
93
  doc << ''
105
- species_lists.each.with_index(1) do |species_list, index|
106
- info = {
107
- birds: birds_info,
108
- plants: plants_info
109
- }[species_list.group]
94
+ index = 1
95
+ groups = species_lists.group_by(&:group)
96
+ [
97
+ OpenStruct.new(
98
+ group: :plants,
99
+ title: 'Pflanzenarten',
100
+ headers: ['Wissenschaftlicher Name', 'Deutscher Name'],
101
+ columns: %i[name name_de]
102
+ ),
103
+ OpenStruct.new(
104
+ group: :birds,
105
+ title: 'Vogelarten',
106
+ headers: ['Deutscher Name', 'Wissenschaftlicher Name'],
107
+ columns: %i[name_de name]
108
+ )
109
+ ].each do |info|
110
+ group = groups.fetch(info.group, [])
111
+ next if group.empty?
112
+
110
113
  doc << "=== #{info.title}"
111
114
  doc << ''
112
- doc << '[cols="1,5,5",options=header]'
113
- doc << '|==='
114
- doc << "|Nr.|#{info.headers.join('|')}"
115
- species_list.each do |species|
116
- doc << "|{counter:species_list#{index}}|#{info.columns.map { |method| species.send(method) }.join('|')}"
115
+ group.each do |species_list|
116
+ caption = '.Tabelle {counter:species_lists}'
117
+ caption << ": #{species_list.description}" if species_list.description
118
+ doc << caption
119
+ doc << '[cols="1,5,5",options=header]'
120
+ doc << '|==='
121
+ doc << "|Nr.|#{info.headers.join('|')}"
122
+ species_list.each do |species|
123
+ doc << "|{counter:species_list#{index}}|#{info.columns.map { |method| species.send(method) }.join('|')}"
124
+ end
125
+ doc << '|==='
126
+ doc << ''
127
+ index += 1
117
128
  end
118
- doc << '|==='
119
- doc << ''
120
129
  end
121
130
  end
122
131
  doc.join("\n")
data/lib/natour/config.rb CHANGED
@@ -7,7 +7,7 @@ module Natour
7
7
  dirs.map do |dir|
8
8
  YAML.safe_load(
9
9
  File.read(Pathname(dir).join(filename)),
10
- [Symbol]
10
+ permitted_classes: [Symbol]
11
11
  )
12
12
  rescue Errno::ENOENT
13
13
  {}
@@ -3,12 +3,19 @@ require 'pathname'
3
3
  require 'asciidoctor'
4
4
  require 'asciidoctor-pdf'
5
5
  require 'vips'
6
+ require 'time'
6
7
 
7
8
  module Natour
8
9
  module_function
9
10
 
10
11
  def convert(filename, out_dir: nil, out_file: nil, overwrite: false,
11
- backend: 'pdf', draft: false, image_maxdim: 16000)
12
+ backend: 'pdf', draft: false, draft_backend: nil, image_maxdim: 16000)
13
+ backend = if draft
14
+ draft_backend || backend
15
+ else
16
+ backend
17
+ end
18
+
12
19
  doc = Asciidoctor.load_file(
13
20
  filename,
14
21
  backend: backend,
@@ -26,10 +33,35 @@ module Natour
26
33
  out_file = Pathname(
27
34
  out_file || "#{doc.attr('docname')}#{doc.attr('outfilesuffix')}"
28
35
  )
36
+ filename = out_dir.join(out_file)
29
37
 
30
38
  if draft
31
39
  doc.find_by(context: :image).each do |node|
32
- node.title = "#{node.title} [#{node.attr('target')}]"
40
+ target = node.attr('target')
41
+ image = Image.load_file(dir.join(target).to_s)
42
+ node.title = "#{node.title} [#{[target, image.date_time].compact.join('|')}]"
43
+ end
44
+ end
45
+
46
+ %w[
47
+ revdate
48
+ docdate
49
+ doctime
50
+ docdatetime
51
+ localdate
52
+ localtime
53
+ localdatetime
54
+ ].each do |attr_name|
55
+ attr_value = doc.attr(attr_name)
56
+ next unless attr_value
57
+
58
+ date_time = Time.parse(attr_value)
59
+ if attr_name.end_with?('datetime')
60
+ doc.set_attr(attr_name, date_time.strftime('%d.%m.%Y %H:%M:%S'))
61
+ elsif attr_name.end_with?('date')
62
+ doc.set_attr(attr_name, date_time.strftime('%d.%m.%Y'))
63
+ elsif attr_name.end_with?('time')
64
+ doc.set_attr(attr_name, date_time.strftime('%H:%M:%S'))
33
65
  end
34
66
  end
35
67
 
@@ -40,32 +72,24 @@ module Natour
40
72
  title_logo_image = doc.attr('title-logo-image')
41
73
  if title_logo_image
42
74
  target = title_logo_image[/^image:{1,2}(.*?)\[(.*?)\]$/, 1]
43
- options = {}
44
- options[:autorotate] = true if target =~ /\.jpe?g$/i
45
- image = Vips::Image.new_from_file(dir.join(target).to_s, options)
46
- scale = image_maxdim / image.size.max.to_f
47
- image = image.resize(scale) if scale < 1.0
75
+ image = Image.load_file(dir.join(target).to_s).autorotate.shrink_to(image_maxdim)
48
76
  new_target = tmp_dir.join("title_logo_image_#{Pathname(target).basename}").to_s
49
- suppress_output { image.write_to_file(new_target) }
77
+ image.save_as(new_target)
50
78
  doc.set_attr('title-logo-image', title_logo_image.gsub(target, new_target))
51
79
  end
52
80
 
53
81
  doc.find_by(context: :image).each.with_index do |node, index|
54
82
  target = node.attr('target')
55
- options = {}
56
- options[:autorotate] = true if target =~ /\.jpe?g$/i
57
- image = Vips::Image.new_from_file(dir.join(target).to_s, options)
58
- scale = image_maxdim / image.size.max.to_f
59
- image = image.resize(scale) if scale < 1.0
83
+ image = Image.load_file(dir.join(target).to_s).autorotate.shrink_to(image_maxdim)
60
84
  new_target = tmp_dir.join("image#{index}_#{Pathname(target).basename}").to_s
61
- suppress_output { image.write_to_file(new_target) }
85
+ image.save_as(new_target)
62
86
  node.set_attr('target', new_target)
63
87
  end
64
88
 
65
89
  FileUtils.mkdir_p(out_dir)
66
90
  mode = File::WRONLY | File::CREAT | File::TRUNC | File::BINARY
67
91
  mode |= File::EXCL unless overwrite
68
- File.open(out_dir.join(out_file), mode) do |file|
92
+ File.open(filename, mode) do |file|
69
93
  doc.write(doc.convert, file)
70
94
  end
71
95
  end
@@ -73,9 +97,11 @@ module Natour
73
97
  FileUtils.mkdir_p(out_dir)
74
98
  mode = File::WRONLY | File::CREAT | File::TRUNC
75
99
  mode |= File::EXCL unless overwrite
76
- File.open(out_dir.join(out_file), mode) do |file|
100
+ File.open(filename, mode) do |file|
77
101
  doc.write(doc.convert, file)
78
102
  end
79
103
  end
104
+
105
+ filename.to_s
80
106
  end
81
107
  end
data/lib/natour/create.rb CHANGED
@@ -8,13 +8,14 @@ module Natour
8
8
  reports = Report.load_directory(
9
9
  dir, track_formats: track_formats, create_map: create_map, map_layers: map_layers
10
10
  )
11
- reports.each.with_index(1) do |report, index|
11
+ reports.map.with_index(1) do |report, index|
12
12
  filename = if index < 2
13
13
  out_dir.join(out_file)
14
14
  else
15
15
  out_dir.join("#{out_file.basename('.*')} (#{index})#{out_file.extname}")
16
16
  end
17
17
  report.save_adoc(filename, overwrite: overwrite, author: adoc_author)
18
+ filename.to_s
18
19
  end
19
20
  end
20
21
  end
@@ -71,10 +71,14 @@ module Natour
71
71
  end
72
72
 
73
73
  def round_duration(duration, hours: 0, minutes: 0, seconds: 0)
74
+ return unless duration
75
+
74
76
  Duration.new(round_multiple_of(duration.to_i, (hours * 60 + minutes) * 60 + seconds))
75
77
  end
76
78
 
77
79
  def round_time(time, hours: 0, minutes: 0, seconds: 0)
80
+ return unless time
81
+
78
82
  Time.at(round_multiple_of(time.to_i, (hours * 60 + minutes) * 60 + seconds))
79
83
  end
80
84
  end
@@ -1,26 +1,29 @@
1
1
  require 'nokogiri'
2
2
  require 'duration'
3
- require 'date'
4
- require 'time'
3
+ require 'timeliness'
5
4
 
6
5
  module Natour
7
6
  class GPXFile < GPSTrack
7
+ GPX_XMLNS = {
8
+ 'xmlns' => 'http://www.topografix.com/GPX/1/1',
9
+ 'xmlns:gpxtrkx' => 'http://www.garmin.com/xmlschemas/TrackStatsExtension/v1'
10
+ }.freeze
11
+
8
12
  def initialize(filename)
9
13
  @doc = Nokogiri.XML(File.read(filename, mode: 'r:utf-8'))
10
14
 
11
- date = Date.parse(@doc.at('/xmlns:gpx/xmlns:metadata/xmlns:time').text)
12
- stats = @doc.at('/xmlns:gpx/xmlns:trk/xmlns:extensions/gpxtrkx:TrackStatsExtension')
15
+ stats = @doc.at('/xmlns:gpx/xmlns:trk/xmlns:extensions/gpxtrkx:TrackStatsExtension', GPX_XMLNS)
13
16
  if stats
14
- ascent = stats.at('./gpxtrkx:Ascent').text.to_i
15
- descent = stats.at('./gpxtrkx:Descent').text.to_i
16
- distance = stats.at('./gpxtrkx:Distance').text.to_i
17
- duration = Duration.new(stats.at('./gpxtrkx:TotalElapsedTime').text.to_i)
17
+ ascent = stats.at('./gpxtrkx:Ascent', GPX_XMLNS).text.to_i
18
+ descent = stats.at('./gpxtrkx:Descent', GPX_XMLNS).text.to_i
19
+ distance = stats.at('./gpxtrkx:Distance', GPX_XMLNS).text.to_i
20
+ duration = Duration.new(stats.at('./gpxtrkx:TotalElapsedTime', GPX_XMLNS).text.to_i)
18
21
  end
19
22
 
20
- start_point = to_track_point(@doc.at('/xmlns:gpx/xmlns:trk/xmlns:trkseg[1]/xmlns:trkpt[1]'))
21
- end_point = to_track_point(@doc.at('/xmlns:gpx/xmlns:trk/xmlns:trkseg[last()]/xmlns:trkpt[last()]'))
23
+ start_point = to_track_point(@doc.at('/xmlns:gpx/xmlns:trk/xmlns:trkseg[1]/xmlns:trkpt[1]', GPX_XMLNS))
24
+ end_point = to_track_point(@doc.at('/xmlns:gpx/xmlns:trk/xmlns:trkseg[last()]/xmlns:trkpt[last()]', GPX_XMLNS))
22
25
 
23
- super(filename, date, ascent, descent, distance, duration, start_point, end_point)
26
+ super(filename, start_point.time&.to_date, ascent, descent, distance, duration, start_point, end_point)
24
27
  end
25
28
 
26
29
  def to_gpx
@@ -33,8 +36,8 @@ module Natour
33
36
  GPSTrackPoint.new(
34
37
  trkpt['lat'].to_f,
35
38
  trkpt['lon'].to_f,
36
- trkpt.at('./xmlns:ele').text.to_f,
37
- Time.parse(trkpt.at('./xmlns:time').text)
39
+ trkpt.at('./xmlns:ele')&.text&.to_f,
40
+ Timeliness.parse(trkpt.at('./xmlns:time')&.text)
38
41
  )
39
42
  end
40
43
  end
data/lib/natour/image.rb CHANGED
@@ -1,29 +1,54 @@
1
1
  require 'vips'
2
2
  require 'timeliness'
3
+ require 'fileutils'
4
+ require 'pathname'
3
5
 
4
6
  module Natour
5
7
  class Image
6
8
  attr_reader :path
7
9
  attr_reader :date_time
8
10
 
9
- def initialize(path)
11
+ def initialize(path, image)
10
12
  @path = path
11
- image = Vips::Image.new_from_file(path)
12
- width, height = image.size
13
- @portrait = width < height
14
- get_field = ->(name) { image.get(name) if image.get_fields.include?(name) }
15
- orientation = get_field.call('exif-ifd0-Orientation')
16
- @portrait = orientation[/^(\d) \(/, 1].to_i.between?(5, 8) if orientation
17
- date_time = get_field.call('exif-ifd0-DateTime')
13
+ @image = image
14
+ @landscape = image.width >= image.height
15
+ orientation = get_field('exif-ifd0-Orientation')
16
+ @landscape = !@landscape if orientation && orientation[/^(\d) \(/, 1].to_i.between?(5, 8)
17
+ date_time = get_field('exif-ifd0-DateTime')
18
18
  @date_time = Timeliness.parse(date_time[/^(.*?) \(/, 1], format: 'yyyy:mm:dd hh:nn:ss') if date_time
19
19
  end
20
20
 
21
- def portrait?
22
- @portrait
21
+ def self.load_file(filename)
22
+ Image.new(filename, Vips::Image.new_from_file(filename))
23
23
  end
24
24
 
25
25
  def landscape?
26
- !portrait?
26
+ @landscape
27
+ end
28
+
29
+ def autorotate
30
+ Image.new(@path, @image.autorot)
31
+ end
32
+
33
+ def shrink_to(maxdim)
34
+ scale = maxdim / @image.size.max.to_f
35
+ image = if scale < 1.0
36
+ @image.resize(scale)
37
+ else
38
+ @image.copy
39
+ end
40
+ Image.new(@path, image)
41
+ end
42
+
43
+ def save_as(filename)
44
+ FileUtils.mkdir_p(Pathname(filename).dirname)
45
+ StdoutUtils.suppress_output { @image.write_to_file(filename) }
46
+ end
47
+
48
+ private
49
+
50
+ def get_field(name)
51
+ @image.get(name) if @image.get_fields.include?(name)
27
52
  end
28
53
  end
29
54
  end
data/lib/natour/report.rb CHANGED
@@ -34,18 +34,22 @@ module Natour
34
34
  path = Pathname(dir)
35
35
  title = Pathname.pwd.basename.to_s.encode('utf-8')
36
36
  .gsub(/^\d{4}-\d{2}-\d{2}( |_|-)?/, '')
37
- images = Pathname.glob('**/*.{jpg,jpeg}', File::FNM_CASEFOLD)
38
- .map { |filename| Image.new(filename.to_s) }
37
+ images = Pathname.glob('**/*.{[j|J][p|P][g|G],[j|J][p|P][e|E][g|G]}')
38
+ .map { |filename| Image.load_file(filename.to_s) }
39
39
  .sort_by { |image| [image.date_time ? 0 : 1, image.date_time, image.path] }
40
40
  species_lists =
41
- Pathname.glob('**/*.{csv,kml}', File::FNM_CASEFOLD)
41
+ Pathname.glob('**/*.{[c|C][s|S][v|V],[k|K][m|M][l|L]}')
42
42
  .map { |filename| SpeciesList.load_file(filename.to_s) }
43
43
  .flatten
44
44
  .sort_by { |species_list| [species_list.type, species_list.date ? 0 : 1, species_list.date] }
45
45
  gps_tracks = if track_formats.empty?
46
46
  []
47
47
  else
48
- Pathname.glob("**/*.{#{track_formats.join(',')}}", File::FNM_CASEFOLD)
48
+ track_patterns = { gpx: '[g|G][p|P][x|X]', fit: '[f|F][i|I][t|T]' }
49
+ track_pattern = track_formats.map { |track_format| track_patterns[track_format] }
50
+ .compact
51
+ .join(',')
52
+ Pathname.glob("**/*.{#{track_pattern}}")
49
53
  .map { |filename| GPSTrack.load_file(filename.to_s) }
50
54
  .sort_by { |gps_track| [gps_track.date, gps_track.path] }
51
55
  end
@@ -58,7 +62,7 @@ module Natour
58
62
  gps_track.save_gpx(track, overwrite: true)
59
63
  filename = Pathname(gps_track.path).sub_ext('.jpg')
60
64
  map.save_image(filename, tracks: [track], layers: map_layers)
61
- Image.new(filename.to_s)
65
+ Image.load_file(filename.to_s)
62
66
  end
63
67
  end
64
68
  end
@@ -24,9 +24,9 @@ module Natour
24
24
  def self.load_file(filename)
25
25
  block = IO.binread(filename, 128)
26
26
  header = if block.unpack('CC') == [0xff, 0xfe]
27
- block[2..-1].force_encoding('utf-16le').encode('utf-8')
27
+ block[2..].force_encoding('utf-16le').encode('utf-8')
28
28
  elsif block.unpack('CCC') == [0xef, 0xbb, 0xbf]
29
- block[3..-1].force_encoding('utf-8')
29
+ block[3..].force_encoding('utf-8')
30
30
  else
31
31
  block
32
32
  end
@@ -34,13 +34,20 @@ module Natour
34
34
  case header
35
35
  when /^Primary/
36
36
  CSV.open(filename, 'r:windows-1252:utf-8', headers: true, liberal_parsing: true) do |csv|
37
- date = DateParser.parse(Pathname(filename).basename).compact.first
37
+ date = DateUtils.parse(Pathname(filename).basename).compact.first
38
38
  items = csv.map { |row| Species.new(row[1], row[0]) }
39
39
  .sort_by(&:name_de).uniq
40
40
  [SpeciesList.new(filename, date, :kosmos_vogelfuehrer, :birds, nil, nil, items)]
41
41
  end
42
+ when /^Name/
43
+ CSV.open(filename, 'r:bom|utf-8', headers: true) do |csv|
44
+ date = DateUtils.parse(Pathname(filename).basename).compact.first
45
+ items = csv.map { |row| Species.new(row[1], row[0]) }
46
+ .sort_by(&:name_de).uniq
47
+ [SpeciesList.new(filename, date, :birdlife_vogelfuehrer, :birds, nil, nil, items)]
48
+ end
42
49
  when /^<\?xml.*?www\.ornitho\.ch/m
43
- date = DateParser.parse(Pathname(filename).basename).compact.first
50
+ date = DateUtils.parse(Pathname(filename).basename).compact.first
44
51
  doc = Nokogiri.XML(File.read(filename, mode: 'r:utf-8'))
45
52
  folder = doc.at('/xmlns:kml/xmlns:Document/xmlns:Folder/xmlns:Folder/xmlns:Folder')
46
53
  name = folder.at('./xmlns:name').text
@@ -49,16 +56,16 @@ module Natour
49
56
  .map { |description| Species.new(*description.scan(/&gt;([^&(]+)&lt;/).flatten.reverse) }
50
57
  .sort_by(&:name_de).uniq
51
58
  [SpeciesList.new(filename, date, :ornitho_ch, :birds, name, nil, items)]
52
- when /^Favoriten/
59
+ when /^(Favoriten|NUMMER_FLORA)/
53
60
  CSV.open(filename, 'r:bom|utf-8', col_sep: ';', skip_blanks: true) do |csv|
54
- chunks = csv.reject { |row| row.count == 1 && row[0] != 'Favoriten' }
55
- .reject { |row| row.count == 4 && row[0] == 'NUMMER_FLORA' }
61
+ chunks = csv.reject { |row| row.count == 1 }
62
+ .map { |row| row[0] == 'NUMMER_FLORA' ? ['Favoriten'] : row }
56
63
  .slice_before { |row| row.count == 1 || row.count == 3 }
57
64
  .reject { |rows| rows.count == 1 }
58
65
  chunks.map do |rows|
59
66
  name, description = rows.shift
60
- date = DateParser.parse(name, Pathname(filename).basename).compact.first
61
- items = rows.map { |row| Species.new(row[1][/^(([^ ]+ [^ ]+)(( aggr\.)|( subsp\. [^ ]+))?)/, 1], row[2]) }
67
+ date = DateUtils.parse(name, Pathname(filename).basename).compact.first
68
+ items = rows.map { |row| Species.new(BotanicalNameUtils.parse(row[1]), row[2]) }
62
69
  .sort_by(&:name).uniq
63
70
  SpeciesList.new(
64
71
  filename,
@@ -73,9 +80,9 @@ module Natour
73
80
  end
74
81
  when /^obs_id/
75
82
  CSV.open(filename, 'r:bom|utf-16le:utf-8', col_sep: "\t", headers: true) do |csv|
76
- date = DateParser.parse(Pathname(filename).basename).compact.first
83
+ date = DateUtils.parse(Pathname(filename).basename).compact.first
77
84
  items = csv.select { |row| row[0] }
78
- .map { |row| Species.new(row[11][/^(([^ ]+ [^ ]+)(( aggr\.)|( subsp\. [^ ]+))?)/, 1], nil) }
85
+ .map { |row| Species.new(BotanicalNameUtils.parse(row[11]), nil) }
79
86
  .sort_by(&:name).uniq
80
87
  [SpeciesList.new(filename, date, :info_flora, :plants, nil, nil, items)]
81
88
  end
@@ -0,0 +1,12 @@
1
+ module Natour
2
+ module BotanicalNameUtils
3
+ module_function
4
+
5
+ def parse(name)
6
+ result = name.match(/^([^ ]+ [^ ]+)(( aggr\.)|(.*( subsp\. [^ ]+)))?.*$/)
7
+ return unless result
8
+
9
+ "#{result[1]}#{result[3]}#{result[5]}"
10
+ end
11
+ end
12
+ end
@@ -1,7 +1,7 @@
1
1
  require 'timeliness'
2
2
 
3
3
  module Natour
4
- module DateParser
4
+ module DateUtils
5
5
  module_function
6
6
 
7
7
  def parse(*args)
@@ -0,0 +1,16 @@
1
+ module Natour
2
+ module StdoutUtils
3
+ module_function
4
+
5
+ def suppress_output
6
+ orig_stdout = $stdout.clone
7
+ orig_stderr = $stderr.clone
8
+ $stdout.reopen(File.new(File::NULL, 'w'))
9
+ $stderr.reopen(File.new(File::NULL, 'w'))
10
+ yield
11
+ ensure
12
+ $stdout.reopen(orig_stdout)
13
+ $stderr.reopen(orig_stderr)
14
+ end
15
+ end
16
+ end
data/lib/natour.rb CHANGED
@@ -1,5 +1,6 @@
1
- require 'natour/helpers/date_parser'
2
- require 'natour/helpers/suppress_output'
1
+ require 'natour/utils/botanical_name_utils'
2
+ require 'natour/utils/date_utils'
3
+ require 'natour/utils/stdout_utils'
3
4
  require 'natour/convert'
4
5
  require 'natour/create'
5
6
  require 'natour/asciinurse'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: natour
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Simon Gysi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-03-07 00:00:00.000000000 Z
11
+ date: 2022-02-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: asciidoctor
@@ -70,16 +70,16 @@ dependencies:
70
70
  name: fit4ruby
71
71
  requirement: !ruby/object:Gem::Requirement
72
72
  requirements:
73
- - - '='
73
+ - - "~>"
74
74
  - !ruby/object:Gem::Version
75
- version: '3.7'
75
+ version: '3.9'
76
76
  type: :runtime
77
77
  prerelease: false
78
78
  version_requirements: !ruby/object:Gem::Requirement
79
79
  requirements:
80
- - - '='
80
+ - - "~>"
81
81
  - !ruby/object:Gem::Version
82
- version: '3.7'
82
+ version: '3.9'
83
83
  - !ruby/object:Gem::Dependency
84
84
  name: nokogiri
85
85
  requirement: !ruby/object:Gem::Requirement
@@ -136,6 +136,20 @@ dependencies:
136
136
  - - "~>"
137
137
  - !ruby/object:Gem::Version
138
138
  version: '0.4'
139
+ - !ruby/object:Gem::Dependency
140
+ name: webrick
141
+ requirement: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - "~>"
144
+ - !ruby/object:Gem::Version
145
+ version: '1.7'
146
+ type: :runtime
147
+ prerelease: false
148
+ version_requirements: !ruby/object:Gem::Requirement
149
+ requirements:
150
+ - - "~>"
151
+ - !ruby/object:Gem::Version
152
+ version: '1.7'
139
153
  - !ruby/object:Gem::Dependency
140
154
  name: word_wrap
141
155
  requirement: !ruby/object:Gem::Requirement
@@ -154,16 +168,16 @@ dependencies:
154
168
  name: rubocop
155
169
  requirement: !ruby/object:Gem::Requirement
156
170
  requirements:
157
- - - "~>"
171
+ - - '='
158
172
  - !ruby/object:Gem::Version
159
- version: '1.2'
173
+ version: '1.25'
160
174
  type: :development
161
175
  prerelease: false
162
176
  version_requirements: !ruby/object:Gem::Requirement
163
177
  requirements:
164
- - - "~>"
178
+ - - '='
165
179
  - !ruby/object:Gem::Version
166
- version: '1.2'
180
+ version: '1.25'
167
181
  description:
168
182
  email: simon.gysi@gmail.com
169
183
  executables:
@@ -192,8 +206,6 @@ files:
192
206
  - lib/natour/gps_track.rb
193
207
  - lib/natour/gps_track_point.rb
194
208
  - lib/natour/gpx_file.rb
195
- - lib/natour/helpers/date_parser.rb
196
- - lib/natour/helpers/suppress_output.rb
197
209
  - lib/natour/image.rb
198
210
  - lib/natour/map_geo_admin.rb
199
211
  - lib/natour/public_transport.rb
@@ -201,7 +213,10 @@ files:
201
213
  - lib/natour/species.rb
202
214
  - lib/natour/species_list.rb
203
215
  - lib/natour/station.rb
204
- homepage: https://rubygems.org/gems/natour
216
+ - lib/natour/utils/botanical_name_utils.rb
217
+ - lib/natour/utils/date_utils.rb
218
+ - lib/natour/utils/stdout_utils.rb
219
+ homepage: https://github.com/simongysi/natour
205
220
  licenses:
206
221
  - MIT
207
222
  metadata:
@@ -216,7 +231,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
216
231
  requirements:
217
232
  - - ">="
218
233
  - !ruby/object:Gem::Version
219
- version: '2.5'
234
+ version: '2.6'
220
235
  required_rubygems_version: !ruby/object:Gem::Requirement
221
236
  requirements:
222
237
  - - ">="
@@ -1,14 +0,0 @@
1
- module Natour
2
- module_function
3
-
4
- def suppress_output
5
- orig_stdout = $stdout.clone
6
- orig_stderr = $stderr.clone
7
- $stdout.reopen(File.new(File::NULL, 'w'))
8
- $stderr.reopen(File.new(File::NULL, 'w'))
9
- yield
10
- ensure
11
- $stdout.reopen(orig_stdout)
12
- $stderr.reopen(orig_stderr)
13
- end
14
- end