geo_works-derivatives 0.4.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.
Files changed (36) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +13 -0
  3. data/.rspec +3 -0
  4. data/.rubocop.yml +22 -0
  5. data/.travis.yml +21 -0
  6. data/Gemfile +7 -0
  7. data/LICENSE.txt +15 -0
  8. data/README.md +63 -0
  9. data/Rakefile +7 -0
  10. data/bin/console +14 -0
  11. data/bin/setup +8 -0
  12. data/config/simpler_tiles.yml +24 -0
  13. data/geo_works-derivatives.gemspec +42 -0
  14. data/lib/geo_works/derivatives/config.rb +78 -0
  15. data/lib/geo_works/derivatives/processors/base_geo_processor.rb +92 -0
  16. data/lib/geo_works/derivatives/processors/gdal.rb +72 -0
  17. data/lib/geo_works/derivatives/processors/image.rb +67 -0
  18. data/lib/geo_works/derivatives/processors/ogr.rb +22 -0
  19. data/lib/geo_works/derivatives/processors/raster/aig.rb +43 -0
  20. data/lib/geo_works/derivatives/processors/raster/base.rb +44 -0
  21. data/lib/geo_works/derivatives/processors/raster/dem.rb +32 -0
  22. data/lib/geo_works/derivatives/processors/raster/info.rb +69 -0
  23. data/lib/geo_works/derivatives/processors/raster.rb +34 -0
  24. data/lib/geo_works/derivatives/processors/rendering.rb +82 -0
  25. data/lib/geo_works/derivatives/processors/vector/base.rb +47 -0
  26. data/lib/geo_works/derivatives/processors/vector/info.rb +89 -0
  27. data/lib/geo_works/derivatives/processors/vector/shapefile.rb +23 -0
  28. data/lib/geo_works/derivatives/processors/vector.rb +31 -0
  29. data/lib/geo_works/derivatives/processors/zip.rb +32 -0
  30. data/lib/geo_works/derivatives/processors.rb +15 -0
  31. data/lib/geo_works/derivatives/runners/raster_derivatives.rb +12 -0
  32. data/lib/geo_works/derivatives/runners/vector_derivatives.rb +12 -0
  33. data/lib/geo_works/derivatives/runners.rb +9 -0
  34. data/lib/geo_works/derivatives/version.rb +6 -0
  35. data/lib/geo_works/derivatives.rb +12 -0
  36. metadata +188 -0
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Raster
6
+ class Base < Hydra::Derivatives::Processors::Processor
7
+ include Hydra::Derivatives::Processors::ShellBasedProcessor
8
+ include GeoWorks::Derivatives::Processors::BaseGeoProcessor
9
+ include GeoWorks::Derivatives::Processors::Image
10
+ include GeoWorks::Derivatives::Processors::Gdal
11
+
12
+ def self.encode(path, options, output_file)
13
+ case options[:label]
14
+ when :thumbnail
15
+ encode_raster(path, output_file, options)
16
+ when :display_raster
17
+ reproject_raster(path, output_file, options)
18
+ end
19
+ end
20
+
21
+ # Set of commands to run to encode the raster thumbnail.
22
+ # @return [Array] set of command name symbols
23
+ def self.encode_queue
24
+ [:translate, :convert, :trim, :center]
25
+ end
26
+
27
+ # Set of commands to run to reproject the raster.
28
+ # @return [Array] set of command name symbols
29
+ def self.reproject_queue
30
+ [:warp, :cloud_optimized_geotiff]
31
+ end
32
+
33
+ def self.encode_raster(in_path, out_path, options)
34
+ run_commands(in_path, out_path, encode_queue, options)
35
+ end
36
+
37
+ def self.reproject_raster(in_path, out_path, options)
38
+ run_commands(in_path, out_path, reproject_queue, options)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Raster
6
+ class Dem < GeoWorks::Derivatives::Processors::Raster::Base
7
+ # Set of commands to run to encode the DEM thumbnail.
8
+ # @return [Array] set of command name symbols
9
+ def self.encode_queue
10
+ [:hillshade, :convert, :trim, :center]
11
+ end
12
+
13
+ # Set of commands to run to reproject the DEM.
14
+ # @return [Array] set of command name symbols
15
+ def self.reproject_queue
16
+ [:hillshade, :warp, :compress]
17
+ end
18
+
19
+ # Executes a gdal hillshade command. Calculates hillshade
20
+ # on a raster that contains elevation data.
21
+ # @param in_path [String] file input path
22
+ # @param out_path [String] processor output file path
23
+ # @param options [Hash] creation options
24
+ def self.hillshade(in_path, out_path, _options)
25
+ execute "gdaldem hillshade -q "\
26
+ "-of GTiff \"#{in_path}\" #{out_path}"
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Raster
6
+ class Info
7
+ attr_accessor :doc
8
+ attr_writer :min_max, :size
9
+
10
+ def initialize(path)
11
+ @doc = gdalinfo(path)
12
+ end
13
+
14
+ # Returns the gdal driver name
15
+ # @return [String] driver name
16
+ def driver
17
+ @driver = driver_name
18
+ end
19
+
20
+ # Returns the min and max values for a raster.
21
+ # @return [String] computed min and max values
22
+ def min_max
23
+ @min_max ||= raster_min_max
24
+ end
25
+
26
+ # Returns the raster size.
27
+ # @return [Array] raster size
28
+ def size
29
+ @size ||= raster_size
30
+ end
31
+
32
+ private
33
+
34
+ # Given an output string from the gdalinfo command, returns
35
+ # the gdal driver used to read dataset.
36
+ # @return [String] gdal driver name
37
+ def driver_name
38
+ match = /(?<=Driver:\s).*?(?=\s)/.match(doc)
39
+ match ? match[0] : ''
40
+ end
41
+
42
+ # Runs the gdalinfo command and returns the result as a string.
43
+ # @param path [String] path to raster file
44
+ # @return [String] output of gdalinfo
45
+ def gdalinfo(path)
46
+ stdout, _stderr, _status = Open3.capture3("gdalinfo -mm #{path}")
47
+ stdout
48
+ end
49
+
50
+ # Given an output string from the gdalinfo command, returns
51
+ # a formatted string for the computed min and max values.
52
+ # @return [String] computed min and max values
53
+ def raster_min_max
54
+ match = %r{(?<=Computed Min/Max=).*?(?=\s)}.match(doc)
55
+ match ? match[0].tr(',', ' ') : ''
56
+ end
57
+
58
+ # Given an output string from the gdalinfo command, returns
59
+ # an array containing the raster width and height as strings.
60
+ # @return [String] raster size
61
+ def raster_size
62
+ match = /(?<=Size is ).*/.match(doc)
63
+ match ? match[0].tr(',', '') : ''
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Raster
6
+ require 'geo_works/derivatives/processors/raster/base'
7
+ require 'geo_works/derivatives/processors/raster/aig'
8
+ require 'geo_works/derivatives/processors/raster/dem'
9
+ require 'geo_works/derivatives/processors/raster/info'
10
+
11
+ class Processor < Hydra::Derivatives::Processors::Processor
12
+ def process
13
+ raster_processor_class.new(source_path,
14
+ directives,
15
+ output_file_service: output_file_service).process
16
+ end
17
+
18
+ # Returns a raster processor class based on mime type passed in the directives object.
19
+ # @return raster processing class
20
+ def raster_processor_class
21
+ case directives.fetch(:input_format)
22
+ when 'text/plain; gdal-format=USGSDEM'
23
+ GeoWorks::Derivatives::Processors::Raster::Dem
24
+ when 'application/octet-stream; gdal-format=AIG'
25
+ GeoWorks::Derivatives::Processors::Raster::Aig
26
+ else
27
+ GeoWorks::Derivatives::Processors::Raster::Base
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+ require 'simpler_tiles'
3
+
4
+ module GeoWorks
5
+ module Derivatives
6
+ module Processors
7
+ module Rendering
8
+ extend ActiveSupport::Concern
9
+ included do
10
+ # Renders a thumbnail from a vector dataset.
11
+ # @param in_path [String] file input path
12
+ # @param out_path [String] processor output file path
13
+ # @param options [Hash] creation options
14
+ def self.vector_thumbnail(in_path, out_path, options)
15
+ map = GeoWorks::Derivatives::Processors::Rendering.simple_tiles_map(in_path, options)
16
+ File.open(out_path, 'wb') { |f| f.write map.to_png }
17
+ end
18
+ end
19
+
20
+ class << self
21
+ # Builds a simple tiles map from a shapefile.
22
+ # @param in_path [String] file input path
23
+ # @param options [Hash] creation
24
+ # @return [SimplerTiles::Map] simple tiles map
25
+ def simple_tiles_map(in_path, options)
26
+ assign_rendering_options(in_path, options)
27
+ size = rendering_size(options)
28
+ SimplerTiles::Map.new do |m|
29
+ m.srs = 'EPSG:4326'
30
+ m.bgcolor = GeoWorks::Derivatives::Config.rendering_config.bg_color
31
+ m.width = size[0]
32
+ m.height = size[1]
33
+ m.set_bounds(*simple_tiles_bounds(options))
34
+ add_shapefile_layer(in_path, m)
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ # Adds a shapefile layer to a simple tiles map.
41
+ # @param in_path [String] file input path
42
+ # @param options [Hash] creation options
43
+ def add_shapefile_layer(in_path, map)
44
+ Dir.glob("#{in_path}/*.shp").each do |shp|
45
+ map.layer shp do |l|
46
+ l.query %(select * from "#{File.basename shp, '.shp'}") do |q|
47
+ q.styles GeoWorks::Derivatives::Config.rendering_config.to_h
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ # Re-orders options bounds for use with a simple tiles map.
54
+ # @param options [Hash] creation options
55
+ # @return [Array] simple tiles map bounds
56
+ def simple_tiles_bounds(options)
57
+ [options[:bounds][:east],
58
+ options[:bounds][:north],
59
+ options[:bounds][:west],
60
+ options[:bounds][:south]]
61
+ end
62
+
63
+ # Transforms the size directive into an array.
64
+ # @param options [Hash] creation options
65
+ # @return [Array] derivative size
66
+ def rendering_size(options)
67
+ options[:output_size].split(' ').map(&:to_i)
68
+ end
69
+
70
+ # Assigns new values from the vector info command to the creation options hash.
71
+ # @param in_path [String] file input path
72
+ # @param options [Hash] creation options
73
+ def assign_rendering_options(in_path, options)
74
+ vector_info = GeoWorks::Derivatives::Processors::Vector::Info.new(in_path)
75
+ options[:name] = vector_info.name
76
+ options[:bounds] = vector_info.bounds
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,47 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Vector
6
+ class Base < Hydra::Derivatives::Processors::Processor
7
+ include Hydra::Derivatives::Processors::ShellBasedProcessor
8
+ include GeoWorks::Derivatives::Processors::BaseGeoProcessor
9
+ include GeoWorks::Derivatives::Processors::Image
10
+ include GeoWorks::Derivatives::Processors::Ogr
11
+ include GeoWorks::Derivatives::Processors::Gdal
12
+ include GeoWorks::Derivatives::Processors::Rendering
13
+ include GeoWorks::Derivatives::Processors::Zip
14
+
15
+ def self.encode(path, options, output_file)
16
+ case options[:label]
17
+ when :thumbnail
18
+ encode_vector(path, output_file, options)
19
+ when :display_vector
20
+ reproject_vector(path, output_file, options)
21
+ end
22
+ end
23
+
24
+ # Set of commands to run to encode the vector thumbnail.
25
+ # @return [Array] set of command name symbols
26
+ def self.encode_queue
27
+ [:reproject, :vector_thumbnail, :trim, :center]
28
+ end
29
+
30
+ # Set of commands to run to reproject the vector.
31
+ # @return [Array] set of command name symbols
32
+ def self.reproject_queue
33
+ [:reproject, :zip]
34
+ end
35
+
36
+ def self.encode_vector(in_path, out_path, options)
37
+ run_commands(in_path, out_path, encode_queue, options)
38
+ end
39
+
40
+ def self.reproject_vector(in_path, out_path, options)
41
+ run_commands(in_path, out_path, reproject_queue, options)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,89 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Vector
6
+ class Info
7
+ attr_accessor :doc
8
+ attr_writer :name, :driver
9
+
10
+ def initialize(path)
11
+ @doc = ogrinfo(path)
12
+ end
13
+
14
+ # Returns the vector dataset name
15
+ # @return [String] dataset name
16
+ def name
17
+ @name = vector_name
18
+ end
19
+
20
+ # Returns the ogr driver name
21
+ # @return [String] driver name
22
+ def driver
23
+ @driver = driver_name
24
+ end
25
+
26
+ # Returns vector geometry type
27
+ # @return [String] geom
28
+ def geom
29
+ @geom = vector_geom
30
+ end
31
+
32
+ # Returns vector bounds
33
+ # @return [String] bounds
34
+ def bounds
35
+ @bounds = vector_bounds
36
+ end
37
+
38
+ private
39
+
40
+ # Runs the ogrinfo command and returns the result as a string.
41
+ # @param path [String] path to vector file or shapefile directory
42
+ # @return [String] output of ogrinfo
43
+ def ogrinfo(path)
44
+ stdout, _stderr, _status = Open3.capture3("ogrinfo -ro -so -al #{path}")
45
+ stdout
46
+ end
47
+
48
+ # Given an output string from the ogrinfo command, returns
49
+ # the vector dataset name.
50
+ # @return [String] vector dataset name
51
+ def vector_name
52
+ match = /(?<=Layer name:\s).*?(?=\n)/.match(doc)
53
+ match ? match[0] : ''
54
+ end
55
+
56
+ # Given an output string from the ogrinfo command, returns
57
+ # the ogr driver used to read dataset.
58
+ # @return [String] ogr driver name
59
+ def driver_name
60
+ match = /(?<=driver\s`).*?(?=')/.match(doc)
61
+ match ? match[0] : ''
62
+ end
63
+
64
+ # Given an output string from the ogrinfo command, returns
65
+ # the vector geometry type.
66
+ # @return [String] vector geom
67
+ def vector_geom
68
+ match = /(?<=Geometry:\s).*?(?=\n)/.match(doc)
69
+ geom = match ? match[0] : ''
70
+ # Transform OGR-style 'Line String' into GeoJSON 'Line'
71
+ geom == 'Line String' ? 'Line' : geom
72
+ end
73
+
74
+ # Given an output string from the ogrinfo command, returns
75
+ # the vector bounding box.
76
+ # @return [Hash] vector bounds
77
+ def vector_bounds
78
+ match = /(?<=Extent:\s).*?(?=\n)/.match(doc)
79
+ extent = match ? match[0] : ''
80
+
81
+ # remove parens and spaces, split into array, and assign elements to variables
82
+ w, s, e, n = extent.delete(' ').gsub(')-(', ',').delete('(').delete(')').split(',')
83
+ { north: n.to_f, east: e.to_f, south: s.to_f, west: w.to_f }
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Vector
6
+ class Shapefile < GeoWorks::Derivatives::Processors::Vector::Base
7
+ include GeoWorks::Derivatives::Processors::Zip
8
+
9
+ def self.encode(path, options, output_file)
10
+ unzip(path, output_file) do |zip_path|
11
+ case options[:label]
12
+ when :thumbnail
13
+ encode_vector(zip_path, output_file, options)
14
+ when :display_vector
15
+ reproject_vector(zip_path, output_file, options)
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Vector
6
+ require 'geo_works/derivatives/processors/vector/base'
7
+ require 'geo_works/derivatives/processors/vector/info'
8
+ require 'geo_works/derivatives/processors/vector/shapefile'
9
+
10
+ class Processor < Hydra::Derivatives::Processors::Processor
11
+ def process
12
+ vector_processor_class.new(source_path,
13
+ directives,
14
+ output_file_service: output_file_service).process
15
+ end
16
+
17
+ # Returns a vector processor class based on mime type passed in the directives object.
18
+ # @return vector processing class
19
+ def vector_processor_class
20
+ case directives.fetch(:input_format)
21
+ when 'application/zip; ogr-format="ESRI Shapefile"'
22
+ GeoWorks::Derivatives::Processors::Vector::Shapefile
23
+ else
24
+ GeoWorks::Derivatives::Processors::Vector::Base
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ module Zip
6
+ extend ActiveSupport::Concern
7
+
8
+ included do
9
+ # Unzips a file, invokes a block, and then deletes the unzipped file(s).
10
+ # Use to wrap processor methods for geo file formats that
11
+ # are zipped before uploading.
12
+ # @param in_path [String] file input path
13
+ # @param output_file [String] processor output file path
14
+ def self.unzip(in_path, output_file, _options = {})
15
+ basename = File.basename(output_file, File.extname(output_file))
16
+ zip_out_path = "#{File.dirname(output_file)}/#{basename}_out"
17
+ execute "unzip -qq -j -d \"#{zip_out_path}\" \"#{in_path}\""
18
+ yield zip_out_path
19
+ FileUtils.rm_rf(zip_out_path)
20
+ end
21
+
22
+ # Zips a file or directory.
23
+ # @param in_path [String] file input path
24
+ # @param output_file [String] output zip file
25
+ def self.zip(in_path, output_file, _options = {})
26
+ execute "zip -j -qq -r \"#{output_file}\" \"#{in_path}\""
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Processors
5
+ require 'geo_works/derivatives/processors/base_geo_processor'
6
+ require 'geo_works/derivatives/processors/gdal'
7
+ require 'geo_works/derivatives/processors/image'
8
+ require 'geo_works/derivatives/processors/ogr'
9
+ require 'geo_works/derivatives/processors/rendering'
10
+ require 'geo_works/derivatives/processors/zip'
11
+ require 'geo_works/derivatives/processors/raster'
12
+ require 'geo_works/derivatives/processors/vector'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Runners
5
+ class RasterDerivatives < Hydra::Derivatives::Runner
6
+ def self.processor_class
7
+ GeoWorks::Derivatives::Processors::Raster::Processor
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Runners
5
+ class VectorDerivatives < Hydra::Derivatives::Runner
6
+ def self.processor_class
7
+ GeoWorks::Derivatives::Processors::Vector::Processor
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ module Runners
5
+ require 'geo_works/derivatives/runners/raster_derivatives'
6
+ require 'geo_works/derivatives/runners/vector_derivatives'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+ module GeoWorks
3
+ module Derivatives
4
+ VERSION = "0.4.0"
5
+ end
6
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+ require 'geo_works/derivatives/version'
3
+ require 'hydra/derivatives'
4
+ require 'mime/types'
5
+
6
+ module GeoWorks
7
+ module Derivatives
8
+ require 'geo_works/derivatives/config'
9
+ require 'geo_works/derivatives/processors'
10
+ require 'geo_works/derivatives/runners'
11
+ end
12
+ end