geo_concerns 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (32) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +2 -0
  3. data/README.md +6 -1
  4. data/app/models/concerns/geo_concerns/file_set/derivatives.rb +5 -5
  5. data/app/models/concerns/geo_concerns/image_file_behavior.rb +1 -1
  6. data/app/models/concerns/geo_concerns/raster_file_behavior.rb +1 -1
  7. data/app/models/concerns/geo_concerns/vector_file_behavior.rb +1 -1
  8. data/app/processors/geo_concerns/processors/base_geo_processor.rb +44 -34
  9. data/app/processors/geo_concerns/processors/gdal.rb +49 -0
  10. data/app/processors/geo_concerns/processors/ogr.rb +19 -0
  11. data/app/processors/geo_concerns/processors/raster/aig.rb +13 -17
  12. data/app/processors/geo_concerns/processors/raster/base.rb +15 -49
  13. data/app/processors/geo_concerns/processors/raster/dem.rb +14 -31
  14. data/app/processors/geo_concerns/processors/raster/info.rb +52 -0
  15. data/app/processors/geo_concerns/processors/raster/processor.rb +0 -1
  16. data/app/processors/geo_concerns/processors/vector/base.rb +16 -42
  17. data/app/processors/geo_concerns/processors/vector/processor.rb +0 -1
  18. data/app/processors/geo_concerns/processors/vector/shapefile.rb +2 -2
  19. data/app/processors/geo_concerns/processors/zip.rb +2 -4
  20. data/geo_concerns.gemspec +7 -8
  21. data/lib/geo_concerns/version.rb +1 -1
  22. data/spec/controllers/image_works_controller_spec.rb +16 -0
  23. data/spec/processors/geo_concerns/processors/base_geo_processor_spec.rb +29 -25
  24. data/spec/processors/geo_concerns/processors/gdal_spec.rb +59 -0
  25. data/spec/processors/geo_concerns/processors/ogr_spec.rb +36 -0
  26. data/spec/processors/geo_concerns/processors/raster/aig_spec.rb +12 -5
  27. data/spec/processors/geo_concerns/processors/raster/base_spec.rb +15 -47
  28. data/spec/processors/geo_concerns/processors/raster/dem_spec.rb +14 -10
  29. data/spec/processors/geo_concerns/processors/raster/info_spec.rb +35 -0
  30. data/spec/processors/geo_concerns/processors/vector/base_spec.rb +15 -28
  31. data/spec/processors/geo_concerns/processors/vector/shapefile_spec.rb +1 -1
  32. metadata +37 -42
@@ -0,0 +1,52 @@
1
+ module GeoConcerns
2
+ module Processors
3
+ module Raster
4
+ class Info
5
+ attr_accessor :doc
6
+ attr_writer :min_max, :size
7
+
8
+ def initialize(path)
9
+ @doc = gdalinfo(path)
10
+ end
11
+
12
+ # Returns the min and max values for a raster.
13
+ # @return [String] computed min and max values
14
+ def min_max
15
+ @min_max ||= raster_min_max
16
+ end
17
+
18
+ # Returns the raster size.
19
+ # @return [Array] raster size
20
+ def size
21
+ @size ||= raster_size
22
+ end
23
+
24
+ private
25
+
26
+ # Runs the gdalinfo command and returns the result as a string.
27
+ # @param path [String] path to raster file
28
+ # @return [String] output of gdalinfo
29
+ def gdalinfo(path)
30
+ stdout, _stderr, _status = Open3.capture3("gdalinfo -mm #{path}")
31
+ stdout
32
+ end
33
+
34
+ # Given an output string from the gdalinfo command, returns
35
+ # a formatted string for the computed min and max values.
36
+ # @return [String] computed min and max values
37
+ def raster_min_max
38
+ match = %r{(?<=Computed Min/Max=).*?(?=\s)}.match(doc)
39
+ match ? match[0].tr(',', ' ') : ''
40
+ end
41
+
42
+ # Given an output string from the gdalinfo command, returns
43
+ # an array containing the raster width and height as strings.
44
+ # @return [String] raster size
45
+ def raster_size
46
+ match = /(?<=Size is ).*/.match(doc)
47
+ match ? match[0].tr(',', '') : ''
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -9,7 +9,6 @@ module GeoConcerns
9
9
  end
10
10
 
11
11
  # Returns a raster processor class based on mime type passed in the directives object.
12
- #
13
12
  # @return raster processing class
14
13
  def raster_processor_class
15
14
  case directives.fetch(:input_format)
@@ -4,63 +4,37 @@ module GeoConcerns
4
4
  class Base < Hydra::Derivatives::Processors::Processor
5
5
  include Hydra::Derivatives::Processors::ShellBasedProcessor
6
6
  include GeoConcerns::Processors::BaseGeoProcessor
7
+ include GeoConcerns::Processors::Ogr
8
+ include GeoConcerns::Processors::Gdal
7
9
  include GeoConcerns::Processors::Zip
8
10
 
9
11
  def self.encode(path, options, output_file)
10
12
  case options[:label]
11
13
  when :thumbnail
12
- encode_vector(path, options, output_file)
14
+ encode_vector(path, output_file, options)
13
15
  when :display_vector
14
- reproject_vector(path, options, output_file)
16
+ reproject_vector(path, output_file, options)
15
17
  end
16
18
  end
17
19
 
18
- def self.encode_vector(in_path, options, out_path)
19
- tiff_path = "#{File.dirname(in_path)}/out.tif"
20
- execute rasterize(in_path, options, tiff_path)
21
- execute translate(tiff_path, options, out_path)
22
- File.unlink(tiff_path)
23
- File.unlink("#{out_path}.aux.xml")
20
+ # Set of commands to run to encode the vector thumbnail.
21
+ # @return [Array] set of command name symbols
22
+ def self.encode_queue
23
+ [:rasterize, :convert]
24
24
  end
25
25
 
26
- def self.reproject_vector(in_path, options, out_path)
27
- shapefile_path = intermediate_shapefile_path(out_path)
28
- execute reproject(in_path, options, shapefile_path)
29
- zip(shapefile_path, out_path)
30
- FileUtils.rm_rf(shapefile_path)
26
+ # Set of commands to run to reproject the vector.
27
+ # @return [Array] set of command name symbols
28
+ def self.reproject_queue
29
+ [:reproject, :zip]
31
30
  end
32
31
 
33
- # Returns a formatted gdal_rasterize command. Used to rasterize vector
34
- # format into raster format.
35
- #
36
- # @param in_path [String] file input path
37
- # #param options [Hash] creation options
38
- # @param out_path [String] processor output file path
39
- # @return [String] command for rasterizing vector dataset
40
- def self.rasterize(in_path, options, out_path)
41
- "gdal_rasterize -q -burn 0 -init 255 -ot Byte -ts "\
42
- "#{options[:output_size]} -of GTiff #{in_path} #{out_path}"
32
+ def self.encode_vector(in_path, out_path, options)
33
+ run_commands(in_path, out_path, encode_queue, options)
43
34
  end
44
35
 
45
- # Returns a formatted ogr2ogr command. Used to reproject a
46
- # vector dataset and save the output as a shapefile.
47
- #
48
- # @param in_path [String] file input path
49
- # #param options [Hash] creation options
50
- # @param out_path [String] processor output file path
51
- # @return [String] command for reprojecting vector data
52
- def self.reproject(in_path, options, out_path)
53
- "env SHAPE_ENCODING= ogr2ogr -q -nln #{options[:basename]} "\
54
- "-f 'ESRI Shapefile' -t_srs #{options[:output_srid]} '#{out_path}' '#{in_path}'"
55
- end
56
-
57
- # Returns a path to an intermediate shape file directory.
58
- #
59
- # @param path [String] file path to base temp path on
60
- # @return [String] tempfile path
61
- def self.intermediate_shapefile_path(path)
62
- ext = File.extname(path)
63
- "#{File.dirname(path)}/#{File.basename(path, ext)}/"
36
+ def self.reproject_vector(in_path, out_path, options)
37
+ run_commands(in_path, out_path, reproject_queue, options)
64
38
  end
65
39
  end
66
40
  end
@@ -9,7 +9,6 @@ module GeoConcerns
9
9
  end
10
10
 
11
11
  # Returns a vector processor class based on mime type passed in the directives object.
12
- #
13
12
  # @return vector processing class
14
13
  def vector_processor_class
15
14
  case directives.fetch(:input_format)
@@ -8,9 +8,9 @@ module GeoConcerns
8
8
  unzip(path, output_file) do |zip_path|
9
9
  case options[:label]
10
10
  when :thumbnail
11
- encode_vector(zip_path, options, output_file)
11
+ encode_vector(zip_path, output_file, options)
12
12
  when :display_vector
13
- reproject_vector(zip_path, options, output_file)
13
+ reproject_vector(zip_path, output_file, options)
14
14
  end
15
15
  end
16
16
  end
@@ -7,10 +7,9 @@ module GeoConcerns
7
7
  # Unzips a file, invokes a block, and then deletes the unzipped file(s).
8
8
  # Use to wrap processor methods for geo file formats that
9
9
  # are zipped before uploading.
10
- #
11
10
  # @param in_path [String] file input path
12
11
  # @param output_file [String] processor output file path
13
- def self.unzip(in_path, output_file)
12
+ def self.unzip(in_path, output_file, _options = {})
14
13
  basename = File.basename(output_file, File.extname(output_file))
15
14
  zip_out_path = "#{File.dirname(output_file)}/#{basename}_out"
16
15
  execute "unzip -qq -j -d \"#{zip_out_path}\" \"#{in_path}\""
@@ -19,10 +18,9 @@ module GeoConcerns
19
18
  end
20
19
 
21
20
  # Zips a file or directory.
22
- #
23
21
  # @param in_path [String] file input path
24
22
  # @param output_file [String] output zip file
25
- def self.zip(in_path, output_file)
23
+ def self.zip(in_path, output_file, _options = {})
26
24
  execute "zip -j -qq -r \"#{output_file}\" \"#{in_path}\""
27
25
  end
28
26
  end
data/geo_concerns.gemspec CHANGED
@@ -18,19 +18,18 @@ Gem::Specification.new do |spec|
18
18
  spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
19
  spec.require_paths = ['lib']
20
20
 
21
- spec.add_dependency 'rails', '~> 4.2.6'
22
- spec.add_dependency 'curation_concerns', '~> 0.14.0.pre1'
23
- spec.add_dependency 'leaflet-rails'
21
+ spec.add_dependency 'curation_concerns', '~> 0.14.0.pre4'
22
+ spec.add_dependency 'leaflet-rails', '~> 0.7'
24
23
 
25
24
  spec.add_development_dependency 'sqlite3'
26
25
  spec.add_development_dependency 'rspec-rails'
27
- spec.add_development_dependency 'engine_cart'
28
- spec.add_development_dependency 'solr_wrapper'
26
+ spec.add_development_dependency 'engine_cart', '~> 0.8'
27
+ spec.add_development_dependency 'solr_wrapper', '~> 0.10'
29
28
  spec.add_development_dependency 'fcrepo_wrapper', '~> 0.1'
30
29
  spec.add_development_dependency 'pry-byebug'
31
30
  spec.add_development_dependency 'database_cleaner', '< 1.1.0'
32
- spec.add_development_dependency 'rubocop'
33
- spec.add_development_dependency 'rubocop-rspec'
31
+ spec.add_development_dependency 'rubocop', '~> 0.39'
32
+ spec.add_development_dependency 'rubocop-rspec', '~> 1.4.1'
34
33
  spec.add_development_dependency 'factory_girl'
35
- spec.add_development_dependency 'capybara'
34
+ spec.add_development_dependency 'capybara', '~> 2.7'
36
35
  end
@@ -1,3 +1,3 @@
1
1
  module GeoConcerns
2
- VERSION = "0.0.1".freeze
2
+ VERSION = "0.0.2".freeze
3
3
  end
@@ -22,4 +22,20 @@ describe CurationConcerns::ImageWorksController, type: :controller do
22
22
  end
23
23
  end
24
24
  end
25
+
26
+ describe '#create' do
27
+ let(:user) { FactoryGirl.create(:admin) }
28
+ before do
29
+ sign_in user
30
+ end
31
+
32
+ context 'when create is successful' do
33
+ let(:work) { FactoryGirl.create(:image_work, user: user) }
34
+ it 'creates an image work' do
35
+ allow(controller).to receive(:curation_concern).and_return(work)
36
+ post :create, image_work: { title: ['a title'] }
37
+ expect(response).to redirect_to main_app.curation_concerns_image_work_path(work)
38
+ end
39
+ end
40
+ end
25
41
  end
@@ -4,6 +4,7 @@ describe GeoConcerns::Processors::BaseGeoProcessor do
4
4
  before do
5
5
  class TestProcessor
6
6
  include GeoConcerns::Processors::BaseGeoProcessor
7
+ include GeoConcerns::Processors::Gdal
7
8
  def directives
8
9
  end
9
10
 
@@ -22,19 +23,38 @@ describe GeoConcerns::Processors::BaseGeoProcessor do
22
23
  let(:directives) { { format: 'png', size: '200x400' } }
23
24
  let(:output_file) { 'output/geo.png' }
24
25
  let(:file_name) { 'files/geo.tif' }
25
- let(:options) { { output_format: 'PNG', output_size: '150 150' } }
26
+ let(:options) { { output_size: '150 150' } }
27
+
28
+ describe '#run_commands' do
29
+ let(:method_queue) { [:translate, :warp, :compress] }
30
+ it 'calls the methods in the queue and cleans up temp files' do
31
+ expect(subject.class).to receive(:translate)
32
+ expect(subject.class).to receive(:warp)
33
+ expect(subject.class).to receive(:compress)
34
+ expect(FileUtils).to receive(:rm_rf).twice
35
+ subject.class.run_commands(file_name, output_file, method_queue, options)
36
+ end
37
+ end
38
+
39
+ describe '#convert' do
40
+ let(:image) { double }
26
41
 
27
- describe '#translate' do
28
- it 'returns a gdal_translate command ' do
29
- expect(subject.class.translate(file_name, options, output_file))
30
- .to include('gdal_translate')
42
+ before do
43
+ allow(MiniMagick::Image).to receive(:open).and_return(image)
44
+ end
45
+
46
+ it 'transforms the image and saves it as a jpeg' do
47
+ expect(image).to receive(:format).with('jpg')
48
+ expect(image).to receive(:combine_options)
49
+ expect(image).to receive(:write).with(output_file)
50
+ subject.class.convert(file_name, output_file, options)
31
51
  end
32
52
  end
33
53
 
34
- describe '#intermediate_file_path' do
54
+ describe '#temp_path' do
35
55
  it 'returns a path to a temporary file based on the input file' do
36
- expect(subject.class.intermediate_file_path(output_file))
37
- .to include('geo_temp.png')
56
+ expect(subject.class.temp_path(output_file))
57
+ .to include('geo')
38
58
  end
39
59
  end
40
60
 
@@ -53,21 +73,6 @@ describe GeoConcerns::Processors::BaseGeoProcessor do
53
73
  end
54
74
  end
55
75
 
56
- describe '#output_format' do
57
- context 'when given jpg as a format' do
58
- let(:directives) { { format: 'jpg' } }
59
- it 'returns JPEG' do
60
- expect(subject.output_format).to eq('JPEG')
61
- end
62
- end
63
-
64
- context 'when given png as a format' do
65
- it 'returns PNG' do
66
- expect(subject.output_format).to eq('PNG')
67
- end
68
- end
69
- end
70
-
71
76
  describe '#output_size' do
72
77
  context 'when given a size string with an x' do
73
78
  it 'returns a size string with a space instead' do
@@ -99,8 +104,7 @@ describe GeoConcerns::Processors::BaseGeoProcessor do
99
104
 
100
105
  describe '#options_for' do
101
106
  it 'returns a hash that includes output size and format' do
102
- expect(subject.options_for("a")).to include(:output_format,
103
- :output_size,
107
+ expect(subject.options_for("a")).to include(:output_size,
104
108
  :label,
105
109
  :output_srid,
106
110
  :basename)
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe GeoConcerns::Processors::BaseGeoProcessor do
4
+ before do
5
+ class TestProcessor
6
+ include Hydra::Derivatives::Processors::ShellBasedProcessor
7
+ include GeoConcerns::Processors::Gdal
8
+ def directives
9
+ end
10
+
11
+ def source_path
12
+ end
13
+ end
14
+
15
+ allow(subject).to receive(:directives).and_return(directives)
16
+ allow(subject).to receive(:source_path).and_return(file_name)
17
+ end
18
+
19
+ after { Object.send(:remove_const, :TestProcessor) }
20
+
21
+ subject { TestProcessor.new }
22
+
23
+ let(:directives) { { format: 'png', size: '200x400' } }
24
+ let(:output_file) { 'output/geo.png' }
25
+ let(:file_name) { 'files/geo.tif' }
26
+ let(:options) { { output_size: '150 150', output_srid: 'EPSG:4326' } }
27
+
28
+ describe '#translate' do
29
+ it 'executes a gdal_translate command' do
30
+ command = "gdal_translate -q -ot Byte -of GTiff \"files/geo.tif\" output/geo.png"
31
+ expect(subject.class).to receive(:execute).with command
32
+ subject.class.translate(file_name, output_file, options)
33
+ end
34
+ end
35
+
36
+ describe '#warp' do
37
+ it 'executes a reproject command' do
38
+ command = "gdalwarp -q -r bilinear -t_srs EPSG:4326 files/geo.tif output/geo.png -co 'COMPRESS=NONE'"
39
+ expect(subject.class).to receive(:execute).with command
40
+ subject.class.warp(file_name, output_file, options)
41
+ end
42
+ end
43
+
44
+ describe '#compress' do
45
+ it 'returns a gdal_translate command with a compress option' do
46
+ command = "gdal_translate -q -ot Byte -a_srs EPSG:4326 files/geo.tif output/geo.png -co 'COMPRESS=LZW'"
47
+ expect(subject.class).to receive(:execute).with command
48
+ subject.class.compress(file_name, output_file, options)
49
+ end
50
+ end
51
+
52
+ describe '#rasterize' do
53
+ it 'executes a rasterize command' do
54
+ command = "gdal_rasterize -q -burn 0 -init 255 -ot Byte -ts 150 150 -of GTiff files/geo.tif output/geo.png"
55
+ expect(subject.class).to receive(:execute).with command
56
+ subject.class.rasterize(file_name, output_file, options)
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,36 @@
1
+ require 'spec_helper'
2
+
3
+ describe GeoConcerns::Processors::BaseGeoProcessor do
4
+ before do
5
+ class TestProcessor
6
+ include Hydra::Derivatives::Processors::ShellBasedProcessor
7
+ include GeoConcerns::Processors::Ogr
8
+ def directives
9
+ end
10
+
11
+ def source_path
12
+ end
13
+ end
14
+
15
+ allow(subject).to receive(:directives).and_return(directives)
16
+ allow(subject).to receive(:source_path).and_return(file_name)
17
+ end
18
+
19
+ after { Object.send(:remove_const, :TestProcessor) }
20
+
21
+ subject { TestProcessor.new }
22
+
23
+ let(:directives) { { format: 'png', size: '200x400' } }
24
+ let(:output_file) { 'output/geo.png' }
25
+ let(:file_name) { 'files/geo.zip' }
26
+ let(:options) { { output_size: '150 150', output_srid: 'EPSG:4326' } }
27
+
28
+ describe '#reproject' do
29
+ it 'executes a reproject command' do
30
+ command = "env SHAPE_ENCODING= ogr2ogr -q -nln -f 'ESRI Shapefile' "\
31
+ "-t_srs EPSG:4326 'output/geo.png' 'files/geo.zip'"
32
+ expect(subject.class).to receive(:execute).with command
33
+ subject.class.reproject(file_name, output_file, options)
34
+ end
35
+ end
36
+ end
@@ -3,8 +3,7 @@ require 'spec_helper'
3
3
  describe GeoConcerns::Processors::Raster::Aig do
4
4
  let(:output_file) { 'output/geo.png' }
5
5
  let(:file_name) { 'files/aig.zip' }
6
- let(:options) { { output_format: 'PNG',
7
- output_size: '150 150',
6
+ let(:options) { { output_size: '150 150',
8
7
  min_max: '2.054 11.717',
9
8
  label: :thumbnail }
10
9
  }
@@ -12,9 +11,17 @@ describe GeoConcerns::Processors::Raster::Aig do
12
11
  subject { described_class.new(file_name, {}) }
13
12
 
14
13
  describe '#translate' do
15
- it 'returns a gdal_translate command with scaling' do
16
- expect(subject.class.translate(file_name, options, output_file))
17
- .to include('-scale 2.054 11.717 255 0')
14
+ it 'executes a gdal_translate command with scaling' do
15
+ command = "gdal_translate -scale 2.054 11.717 255 0 -q -ot Byte -of "\
16
+ "GTiff \"files/aig.zip\" output/geo.png"
17
+ expect(subject.class).to receive(:execute).with command
18
+ subject.class.translate(file_name, output_file, options)
19
+ end
20
+ end
21
+
22
+ describe '#reproject_queue' do
23
+ it 'returns an array of command name symbols' do
24
+ expect(subject.class.reproject_queue).to include :warp
18
25
  end
19
26
  end
20
27