natour 0.3.0 → 0.7.0

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.
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