grassgis 0.4.3 → 0.5.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 +4 -4
- data/.gitignore +22 -22
- data/.travis.yml +7 -7
- data/Gemfile +4 -4
- data/LICENSE.txt +22 -22
- data/README.md +722 -578
- data/Rakefile +22 -22
- data/Vagrantfile +19 -18
- data/grassgis.gemspec +28 -28
- data/lib/grassgis.rb +10 -8
- data/lib/grassgis/context.rb +2 -0
- data/lib/grassgis/cookbook.rb +483 -0
- data/lib/grassgis/error.rb +4 -4
- data/lib/grassgis/location.rb +53 -53
- data/lib/grassgis/mapset.rb +31 -31
- data/lib/grassgis/module.rb +73 -73
- data/lib/grassgis/support.rb +26 -26
- data/lib/grassgis/tools.rb +215 -0
- data/lib/grassgis/version.rb +3 -3
- metadata +5 -3
data/Rakefile
CHANGED
@@ -1,22 +1,22 @@
|
|
1
|
-
require "bundler/gem_tasks"
|
2
|
-
|
3
|
-
require 'rake/testtask'
|
4
|
-
Rake::TestTask.new(:test) do |test|
|
5
|
-
test.libs << 'lib' << 'test'
|
6
|
-
test.pattern = 'test/**/test_*.rb'
|
7
|
-
test.verbose = true
|
8
|
-
end
|
9
|
-
|
10
|
-
require 'rdoc/task'
|
11
|
-
Rake::RDocTask.new do |rdoc|
|
12
|
-
version = GrassGis::VERSION
|
13
|
-
|
14
|
-
rdoc.rdoc_dir = 'rdoc'
|
15
|
-
rdoc.title = "GrassGis #{version}"
|
16
|
-
rdoc.main = "README.md"
|
17
|
-
rdoc.rdoc_files.include('README*')
|
18
|
-
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
-
rdoc.markup = 'markdown' if rdoc.respond_to?(:markup)
|
20
|
-
end
|
21
|
-
|
22
|
-
task :default => :test
|
1
|
+
require "bundler/gem_tasks"
|
2
|
+
|
3
|
+
require 'rake/testtask'
|
4
|
+
Rake::TestTask.new(:test) do |test|
|
5
|
+
test.libs << 'lib' << 'test'
|
6
|
+
test.pattern = 'test/**/test_*.rb'
|
7
|
+
test.verbose = true
|
8
|
+
end
|
9
|
+
|
10
|
+
require 'rdoc/task'
|
11
|
+
Rake::RDocTask.new do |rdoc|
|
12
|
+
version = GrassGis::VERSION
|
13
|
+
|
14
|
+
rdoc.rdoc_dir = 'rdoc'
|
15
|
+
rdoc.title = "GrassGis #{version}"
|
16
|
+
rdoc.main = "README.md"
|
17
|
+
rdoc.rdoc_files.include('README*')
|
18
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
19
|
+
rdoc.markup = 'markdown' if rdoc.respond_to?(:markup)
|
20
|
+
end
|
21
|
+
|
22
|
+
task :default => :test
|
data/Vagrantfile
CHANGED
@@ -1,18 +1,19 @@
|
|
1
|
-
# -*- mode: ruby -*-
|
2
|
-
# vi: set ft=ruby :
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
sudo add-apt-repository -y ppa:
|
10
|
-
sudo apt-
|
11
|
-
sudo apt-get
|
12
|
-
sudo apt-get install -y grass7
|
13
|
-
sudo apt-get install -y
|
14
|
-
sudo apt-get install -y
|
15
|
-
sudo apt-get install -y
|
16
|
-
sudo
|
17
|
-
|
18
|
-
|
1
|
+
# -*- mode: ruby -*-
|
2
|
+
# vi: set ft=ruby :
|
3
|
+
|
4
|
+
# GRASS 7 environment on Ubuntu for tests
|
5
|
+
Vagrant.configure(2) do |config|
|
6
|
+
config.vm.box = "ubuntu/trusty32"
|
7
|
+
|
8
|
+
config.vm.provision "shell", inline: <<-SHELL
|
9
|
+
sudo add-apt-repository -y ppa:ubuntugis/ubuntugis-unstable
|
10
|
+
sudo add-apt-repository -y ppa:grass/grass-stable
|
11
|
+
sudo apt-get update -y
|
12
|
+
sudo apt-get install -y grass7
|
13
|
+
sudo apt-get install -y grass7-dev
|
14
|
+
sudo apt-get install -y build-essential
|
15
|
+
sudo apt-get install -y ruby-dev
|
16
|
+
sudo apt-get install -y libsqlite3-dev
|
17
|
+
sudo gem install bundler
|
18
|
+
SHELL
|
19
|
+
end
|
data/grassgis.gemspec
CHANGED
@@ -1,28 +1,28 @@
|
|
1
|
-
# coding: utf-8
|
2
|
-
lib = File.expand_path('../lib', __FILE__)
|
3
|
-
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
-
require 'grassgis/version'
|
5
|
-
|
6
|
-
Gem::Specification.new do |spec|
|
7
|
-
spec.name = "grassgis"
|
8
|
-
spec.version = GrassGis::VERSION
|
9
|
-
spec.authors = ["Javier Goizueta"]
|
10
|
-
spec.email = ["jgoizueta@gmail.com"]
|
11
|
-
spec.summary = %q{Support for scripting GRASS GIS in Ruby}
|
12
|
-
spec.description = %q{Support for scripting GRASS GIS in Ruby.}
|
13
|
-
spec.homepage = "https://github.com/jgoizueta/grassgis"
|
14
|
-
spec.license = "MIT"
|
15
|
-
|
16
|
-
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
-
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
-
spec.require_paths = ["lib"]
|
20
|
-
|
21
|
-
spec.add_dependency "sys_cmd", "~> 1.1"
|
22
|
-
|
23
|
-
spec.add_development_dependency "bundler", "~> 1.9"
|
24
|
-
spec.add_development_dependency "rake"
|
25
|
-
spec.add_development_dependency "minitest", "~> 5.4"
|
26
|
-
|
27
|
-
spec.required_ruby_version = '>= 1.9.3'
|
28
|
-
end
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'grassgis/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "grassgis"
|
8
|
+
spec.version = GrassGis::VERSION
|
9
|
+
spec.authors = ["Javier Goizueta"]
|
10
|
+
spec.email = ["jgoizueta@gmail.com"]
|
11
|
+
spec.summary = %q{Support for scripting GRASS GIS in Ruby}
|
12
|
+
spec.description = %q{Support for scripting GRASS GIS in Ruby.}
|
13
|
+
spec.homepage = "https://github.com/jgoizueta/grassgis"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_dependency "sys_cmd", "~> 1.1"
|
22
|
+
|
23
|
+
spec.add_development_dependency "bundler", "~> 1.9"
|
24
|
+
spec.add_development_dependency "rake"
|
25
|
+
spec.add_development_dependency "minitest", "~> 5.4"
|
26
|
+
|
27
|
+
spec.required_ruby_version = '>= 1.9.3'
|
28
|
+
end
|
data/lib/grassgis.rb
CHANGED
@@ -1,8 +1,10 @@
|
|
1
|
-
require 'sys_cmd'
|
2
|
-
require 'grassgis/version'
|
3
|
-
require 'grassgis/error'
|
4
|
-
require 'grassgis/support'
|
5
|
-
require 'grassgis/
|
6
|
-
require 'grassgis/
|
7
|
-
require 'grassgis/
|
8
|
-
require 'grassgis/
|
1
|
+
require 'sys_cmd'
|
2
|
+
require 'grassgis/version'
|
3
|
+
require 'grassgis/error'
|
4
|
+
require 'grassgis/support'
|
5
|
+
require 'grassgis/tools'
|
6
|
+
require 'grassgis/module'
|
7
|
+
require 'grassgis/location'
|
8
|
+
require 'grassgis/mapset'
|
9
|
+
require 'grassgis/context'
|
10
|
+
require 'grassgis/cookbook'
|
data/lib/grassgis/context.rb
CHANGED
@@ -30,6 +30,7 @@ module GrassGis
|
|
30
30
|
config[:echo] = :commands unless config.key?(:echo)
|
31
31
|
|
32
32
|
@config = config
|
33
|
+
@tools = (config[:tools] == false) ? false : true
|
33
34
|
|
34
35
|
locals = config[:locals] || {}
|
35
36
|
locals.each do |var_name, value|
|
@@ -119,6 +120,7 @@ module GrassGis
|
|
119
120
|
insert_path 'PATH', *paths
|
120
121
|
insert_path 'MANPATH', File.join(@config[:gisbase], 'man')
|
121
122
|
@history = []
|
123
|
+
extend GrassGis::Tools if @tools
|
122
124
|
end
|
123
125
|
|
124
126
|
def dispose
|
@@ -0,0 +1,483 @@
|
|
1
|
+
# Recipes can only be defined when input parameters, files and maps are specific
|
2
|
+
# (although an input file can be a directory with an indetermninate number of files inside)
|
3
|
+
# and there's no dependency between input parameters/files/maps and which products are
|
4
|
+
# generated. (so the generated products are also specific, except, again, for the contents
|
5
|
+
# of directories).
|
6
|
+
# Also, recipes shouldn't generate any non-declared maps, i.e. temporary or
|
7
|
+
# auxiliary maps should be removed before the recipe ends.
|
8
|
+
#
|
9
|
+
# Temporary maps & files support, e.g.: temporaries are declared,
|
10
|
+
# then recipe is executed in a block with a ensure which removes temporary maps
|
11
|
+
#
|
12
|
+
# Example:
|
13
|
+
#
|
14
|
+
# GrassCookbook.recipe :dem_base_from_mdt05 do
|
15
|
+
# description %{
|
16
|
+
# Generate a DEM for the interest area at fixed 5m resolution
|
17
|
+
# from CNIG's MDT05 data.
|
18
|
+
# }
|
19
|
+
#
|
20
|
+
# required_files 'data/MDT05'
|
21
|
+
# generated_raster_maps 'dem_base'
|
22
|
+
#
|
23
|
+
# process do |mdt05_sheets|
|
24
|
+
# ...
|
25
|
+
# end
|
26
|
+
# end
|
27
|
+
#
|
28
|
+
# GrassCookbook.recipe :dem_base_derived do
|
29
|
+
# description %{
|
30
|
+
# Generate DEM-derived maps from the 5m dem base:
|
31
|
+
# slope, aspect and relief shading
|
32
|
+
# }
|
33
|
+
#
|
34
|
+
# required_raster_maps 'dem_base'
|
35
|
+
# generated_raster_maps 'shade_base', 'slope_base', 'aspect_base'
|
36
|
+
#
|
37
|
+
# process do
|
38
|
+
# r.relief input: 'dem_base', output: 'shade_base'
|
39
|
+
# r.slope.aspect elevation: 'dem_base',
|
40
|
+
# slope: 'slope_base',
|
41
|
+
# aspect: 'aspect_base'
|
42
|
+
# end
|
43
|
+
# end
|
44
|
+
#
|
45
|
+
# GrassCookbook.recipe :working_dem do
|
46
|
+
# description %{
|
47
|
+
# Generate DEM data at working resolution
|
48
|
+
# }
|
49
|
+
#
|
50
|
+
# required_raster_maps 'dem_base', 'slope_base', 'aspect_base'
|
51
|
+
# generated_raster_maps 'dem', 'slope', 'aspect'
|
52
|
+
#
|
53
|
+
# process do |resolution|
|
54
|
+
# ...
|
55
|
+
# end
|
56
|
+
# end
|
57
|
+
#
|
58
|
+
# # Now use our recipes to compute some permanent base maps and
|
59
|
+
# # alternative scenario mapsheets varying some parameter.
|
60
|
+
#
|
61
|
+
#
|
62
|
+
# GrassGis.session grass_config do
|
63
|
+
# # First generate maps using fixed parameters and move them to PERMANENT
|
64
|
+
# fixed_parameters = { mdt05_sheets: %w(0244 0282) }
|
65
|
+
# fixed_data = primary = GrassCookbook::Data[
|
66
|
+
# parameters: fixed_parameters.keys,
|
67
|
+
# files: GrassCookbook.existing_external_input_files
|
68
|
+
# ]
|
69
|
+
# plan = GrassCookbook.plan(fixed_data)
|
70
|
+
# permanent = plan.last
|
71
|
+
# GrassCookbook.replace_existing_products self, plan
|
72
|
+
# GrassCookbook.execute self, fixed_parameters, plan
|
73
|
+
# permanent.maps.each do |map, type|
|
74
|
+
# move_map(map, type: type, to: 'PERMANENT')
|
75
|
+
# end
|
76
|
+
#
|
77
|
+
# # Then define some variations of other parameters and create a mapset
|
78
|
+
# # for each variation, where maps dependent on the varying parameters
|
79
|
+
# # will be put
|
80
|
+
# variants = {
|
81
|
+
# '10m' => { resolucion: 10 },
|
82
|
+
# '25m' => { resolucion: 25 }
|
83
|
+
# }
|
84
|
+
# for variant_name, variant_parameters in variants
|
85
|
+
# data = GrassCookbook::Data[parameters: variant_parameters.keys] + permanent
|
86
|
+
# plan = GrassCookbook.plan(data)
|
87
|
+
# GrassCookbook.replace_existing_products self, plan
|
88
|
+
# GrassCookbook.execute self, fixed_parameters.merge(variant_parameters), plan
|
89
|
+
# variant_maps = (plan.last - data).maps
|
90
|
+
# create_mapset variant_name
|
91
|
+
# variant_maps.each do |map, type|
|
92
|
+
# move_map(map, type: type, to: variant_name)
|
93
|
+
# end
|
94
|
+
# end
|
95
|
+
# end
|
96
|
+
#
|
97
|
+
#
|
98
|
+
module GrassCookbook
|
99
|
+
|
100
|
+
# Datasets used by recipes, consist of parmeters, maps and files
|
101
|
+
# (or directories). Recipes use them to define both required and
|
102
|
+
# generated data.
|
103
|
+
class Data
|
104
|
+
def initialize(params = {})
|
105
|
+
@parameters = params[:parameters] || []
|
106
|
+
@files = params[:files] || []
|
107
|
+
@maps = params[:maps] || []
|
108
|
+
end
|
109
|
+
|
110
|
+
attr_reader :parameters, :files, :maps
|
111
|
+
|
112
|
+
def vector_maps
|
113
|
+
@maps.select { |m, t| t == :vector }.map(&:first)
|
114
|
+
end
|
115
|
+
|
116
|
+
def raster_maps
|
117
|
+
@maps.select { |m, t| t == :raster }
|
118
|
+
end
|
119
|
+
|
120
|
+
def merge!(data)
|
121
|
+
data = Data[data]
|
122
|
+
@parameters = (@parameters + data.parameters).uniq
|
123
|
+
@maps = (@maps + data.maps).uniq
|
124
|
+
@files = (@files + data.files).uniq
|
125
|
+
self
|
126
|
+
end
|
127
|
+
|
128
|
+
def dup
|
129
|
+
Data.new.merge! self
|
130
|
+
end
|
131
|
+
|
132
|
+
def self.[](params)
|
133
|
+
unless params.is_a?(Data)
|
134
|
+
params = Data.new(params)
|
135
|
+
end
|
136
|
+
params
|
137
|
+
end
|
138
|
+
|
139
|
+
def present?
|
140
|
+
@parameters.size > 0 || @files.size > 0 || @maps.size > 0
|
141
|
+
end
|
142
|
+
|
143
|
+
def empty?
|
144
|
+
!present?
|
145
|
+
end
|
146
|
+
|
147
|
+
def -(other)
|
148
|
+
other = Data[other]
|
149
|
+
Data[
|
150
|
+
parameters: parameters - other.parameters,
|
151
|
+
files: files - other.files,
|
152
|
+
maps: maps - other.maps
|
153
|
+
]
|
154
|
+
end
|
155
|
+
|
156
|
+
def +(other)
|
157
|
+
dup.merge! other
|
158
|
+
end
|
159
|
+
|
160
|
+
# data which is requiered but not provided here
|
161
|
+
def missing(required)
|
162
|
+
Data[required] - self
|
163
|
+
end
|
164
|
+
|
165
|
+
def to_s
|
166
|
+
txt = "Datos:\n"
|
167
|
+
txt << " Parametros: #{parameters.inspect}\n"
|
168
|
+
txt << " Archivos: #{files.inspect}\n"
|
169
|
+
txt << " Mapas: #{maps.inspect}\n"
|
170
|
+
txt
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
@recipes = {}
|
175
|
+
|
176
|
+
# A recipe uses some Data and generates other Data in
|
177
|
+
# a GRASS session.
|
178
|
+
class Recipe
|
179
|
+
def initialize(options = {}, &blk)
|
180
|
+
@id = options[:id].to_sym
|
181
|
+
@description = options[:description] || "Proceso #{@id}"
|
182
|
+
@process = blk
|
183
|
+
@required_parameters = blk.parameters.map(&:last)
|
184
|
+
@required_raster_maps = Array(options[:required_raster_maps])
|
185
|
+
@required_vector_maps = Array(options[:required_vector_maps])
|
186
|
+
@required_files = Array(options[:required_files])
|
187
|
+
@generated_vector_maps = Array(options[:generated_vector_maps])
|
188
|
+
@generated_raster_maps = Array(options[:generated_raster_maps])
|
189
|
+
@generated_files = Array(options[:generated_files])
|
190
|
+
@generated_parameters = Array(options[:generated_parameters])
|
191
|
+
end
|
192
|
+
|
193
|
+
attr_reader :id, :description
|
194
|
+
attr_reader :required_raster_maps, :required_vector_maps, :required_files
|
195
|
+
attr_reader :required_parameters
|
196
|
+
attr_reader :generated_raster_maps, :generated_vector_maps, :generated_files
|
197
|
+
attr_reader :generated_parameters
|
198
|
+
|
199
|
+
# inputs
|
200
|
+
def requirements
|
201
|
+
Data[parameters: required_parameters, files: required_files, maps: required_maps]
|
202
|
+
end
|
203
|
+
|
204
|
+
# outputs
|
205
|
+
def products
|
206
|
+
Data[parameters: generated_parameters, files: generated_files, maps: generated_maps]
|
207
|
+
end
|
208
|
+
|
209
|
+
def required_maps
|
210
|
+
required_raster_maps.map { |map| [map, :raster] } +
|
211
|
+
required_vector_maps.map { |map| [map, :vector] }
|
212
|
+
end
|
213
|
+
|
214
|
+
def generated_maps
|
215
|
+
generated_raster_maps.map { |map| [map, :raster] } +
|
216
|
+
generated_vector_maps.map { |map| [map, :vector] }
|
217
|
+
end
|
218
|
+
|
219
|
+
# Can the recipe be done given provided input?
|
220
|
+
def doable?(input)
|
221
|
+
# input.missing(requirements).empty?
|
222
|
+
(requirements - input).empty?
|
223
|
+
end
|
224
|
+
|
225
|
+
# Is the recipe unnecessary given existing data?
|
226
|
+
def done?(existing)
|
227
|
+
(products - existing).empty?
|
228
|
+
end
|
229
|
+
|
230
|
+
# Execute the recipe with given parameters # TODO: mapset PATH
|
231
|
+
def cook(grass, parameters = {})
|
232
|
+
# grass.g.mapsets '-p'
|
233
|
+
# current_mapset = output.lines.last.split.first
|
234
|
+
|
235
|
+
# Leave planning/checking if doable to the caller; not done here:
|
236
|
+
# unless doable? GrassCookbook.available_data(grass, parameters)
|
237
|
+
# raise "Requirements for #{@id} not fulfilled"
|
238
|
+
# end
|
239
|
+
|
240
|
+
# Also not done here:
|
241
|
+
# if done? GrassCookbook.available_data(grass, parameters)
|
242
|
+
# return
|
243
|
+
# end
|
244
|
+
|
245
|
+
args = parameters.values_at(*@required_parameters)
|
246
|
+
# @process.call grass, *args
|
247
|
+
# grass.session &@process, arguments: args
|
248
|
+
# TODO: support in GrassGis for injecting/replacing locals
|
249
|
+
grass.define_singleton_method(:parameters) { @parameters }
|
250
|
+
grass.instance_exec *args, &@process
|
251
|
+
end
|
252
|
+
|
253
|
+
def to_s
|
254
|
+
@description
|
255
|
+
end
|
256
|
+
|
257
|
+
def inspect
|
258
|
+
"<GrassCookbook::Recipe #{@id.inspect}>"
|
259
|
+
end
|
260
|
+
|
261
|
+
def eql?(other)
|
262
|
+
@id == other.id
|
263
|
+
end
|
264
|
+
|
265
|
+
def ==(other)
|
266
|
+
eql? other
|
267
|
+
end
|
268
|
+
|
269
|
+
def hash
|
270
|
+
@id.hash
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
# DSL to define recipes
|
275
|
+
class RecipeDsl
|
276
|
+
def initialize(id)
|
277
|
+
@id = id.to_sym
|
278
|
+
@required_raster_maps = []
|
279
|
+
@required_vector_maps = []
|
280
|
+
@required_files = []
|
281
|
+
@generated_raster_maps = []
|
282
|
+
@generated_vector_maps = []
|
283
|
+
@generated_files = []
|
284
|
+
@generated_parameters = []
|
285
|
+
@description = nil
|
286
|
+
end
|
287
|
+
|
288
|
+
def description(text)
|
289
|
+
@description = GrassGis::Support.unindent(text)
|
290
|
+
end
|
291
|
+
|
292
|
+
def required_raster_maps(*maps)
|
293
|
+
@required_raster_maps += maps
|
294
|
+
end
|
295
|
+
|
296
|
+
def required_vector_maps(*maps)
|
297
|
+
@required_vector_maps += maps
|
298
|
+
end
|
299
|
+
|
300
|
+
def required_files(*files)
|
301
|
+
@required_files += files
|
302
|
+
end
|
303
|
+
|
304
|
+
def generated_vector_maps(*maps)
|
305
|
+
@generated_vector_maps += maps
|
306
|
+
end
|
307
|
+
|
308
|
+
def generated_raster_maps(*maps)
|
309
|
+
@generated_raster_maps += maps
|
310
|
+
end
|
311
|
+
|
312
|
+
def generated_files(*files)
|
313
|
+
@generated_files += files
|
314
|
+
end
|
315
|
+
|
316
|
+
def generated_parameters(*parameters)
|
317
|
+
@generated_parameters += parameters
|
318
|
+
end
|
319
|
+
|
320
|
+
def process(&blk)
|
321
|
+
@process = blk
|
322
|
+
end
|
323
|
+
|
324
|
+
def recipe
|
325
|
+
Recipe.new(
|
326
|
+
id: @id,
|
327
|
+
required_maps: @required_maps,
|
328
|
+
required_files: @required_files,
|
329
|
+
generated_raster_maps: @generated_raster_maps,
|
330
|
+
generated_vector_maps: @generated_vector_maps,
|
331
|
+
generated_files: @generated_files,
|
332
|
+
generated_parameters: @generated_parameters,
|
333
|
+
&@process
|
334
|
+
)
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
class <<self
|
339
|
+
def recipe(id, &blk)
|
340
|
+
dsl = RecipeDsl.new(id)
|
341
|
+
dsl.instance_eval &blk
|
342
|
+
@recipes[id.to_sym] = dsl.recipe
|
343
|
+
end
|
344
|
+
|
345
|
+
def [](recipe)
|
346
|
+
if recipe.is_a? Recipe
|
347
|
+
recipe
|
348
|
+
else
|
349
|
+
@recipes[recipe.to_sym]
|
350
|
+
end
|
351
|
+
end
|
352
|
+
|
353
|
+
def cook(grass, recipe, parameters)
|
354
|
+
grass.log "Recipe: #{recipe}"
|
355
|
+
self[recipe].cook grass, parameters
|
356
|
+
end
|
357
|
+
|
358
|
+
def all_files_used
|
359
|
+
@recipes.values.map(&:required_files).flatten.uniq
|
360
|
+
end
|
361
|
+
|
362
|
+
def all_maps_used
|
363
|
+
@recipes.values.map(&:required_maps).flatten(1).uniq
|
364
|
+
end
|
365
|
+
|
366
|
+
def all_files_possible
|
367
|
+
@recipes.values.map(&:generated_files).flatten.uniq
|
368
|
+
end
|
369
|
+
|
370
|
+
def all_maps_possible
|
371
|
+
@recipes.values.map(&:generated_maps).flatten(1).uniq
|
372
|
+
end
|
373
|
+
|
374
|
+
def existing_input_files
|
375
|
+
all_files_used.select { |f| File.exists?(f) }
|
376
|
+
end
|
377
|
+
|
378
|
+
def existing_input_maps(grass)
|
379
|
+
# TODO: use mapset PATH here (add as another parameters)
|
380
|
+
path = []
|
381
|
+
all_maps_used.select { |m, t| grass.map_exists?(m, type: t, mapset: path) }
|
382
|
+
end
|
383
|
+
|
384
|
+
# Generate ordered recipes and generated products (output)
|
385
|
+
# than can be obtained with available inputdata.
|
386
|
+
def plan(input_data)
|
387
|
+
input = Data[input_data]
|
388
|
+
existing = input.dup
|
389
|
+
|
390
|
+
applied_recipes = []
|
391
|
+
|
392
|
+
remaining_recipes = @recipes.values - applied_recipes
|
393
|
+
|
394
|
+
while remaining_recipes.size > 0
|
395
|
+
progress = false
|
396
|
+
remaining_recipes.each do |recipe|
|
397
|
+
unless recipe.done?(existing)
|
398
|
+
if recipe.doable?(existing)
|
399
|
+
progress = true
|
400
|
+
applied_recipes << recipe
|
401
|
+
existing.merge! recipe.products
|
402
|
+
end
|
403
|
+
end
|
404
|
+
end
|
405
|
+
break unless progress
|
406
|
+
remaining_recipes -= applied_recipes
|
407
|
+
end
|
408
|
+
[applied_recipes, existing - input]
|
409
|
+
end
|
410
|
+
|
411
|
+
def available_data(grass, parameters)
|
412
|
+
Data[
|
413
|
+
parameters: parameters,
|
414
|
+
files: existing_input_files,
|
415
|
+
maps: existing_input_maps(grass)
|
416
|
+
]
|
417
|
+
end
|
418
|
+
|
419
|
+
def achievable_results(grass, parameters)
|
420
|
+
inputs = available_data(grass, parameters)
|
421
|
+
inputs + plan(inputs).last
|
422
|
+
end
|
423
|
+
|
424
|
+
# primary input files
|
425
|
+
# input files that exist (and are not generated, so they are externally provided input)
|
426
|
+
def existing_external_input_files
|
427
|
+
existing_input_files - all_files_possible
|
428
|
+
end
|
429
|
+
|
430
|
+
def permantent_results(grass, fixed_parameters)
|
431
|
+
primary = Data[parameters: fixed_parameters, files: existing_external_input_files]
|
432
|
+
plan(primary).last
|
433
|
+
end
|
434
|
+
|
435
|
+
def missing_input(grass)
|
436
|
+
Data[
|
437
|
+
files: all_files_used - existing_input_files,
|
438
|
+
maps: all_maps_used - existing_input_maps(grass)
|
439
|
+
]
|
440
|
+
end
|
441
|
+
|
442
|
+
def impossible_results(grass, parameters)
|
443
|
+
possibilities = Data[
|
444
|
+
files: all_files_possible,
|
445
|
+
maps: all_maps_possible
|
446
|
+
]
|
447
|
+
possibilities - achievable_results(grass, parameters)
|
448
|
+
end
|
449
|
+
|
450
|
+
def execute(grass, parameters, plan)
|
451
|
+
recipes, result = plan
|
452
|
+
recipes.each do |recipe|
|
453
|
+
cook grass, recipe, parameters
|
454
|
+
end
|
455
|
+
result
|
456
|
+
end
|
457
|
+
|
458
|
+
def replace_existing_products(grass, plan, parameters = nil)
|
459
|
+
results = plan.last
|
460
|
+
if parameters
|
461
|
+
results.parameters.each do |parameter|
|
462
|
+
parameters.delete parameter
|
463
|
+
end
|
464
|
+
end
|
465
|
+
results.maps.each do |map, type|
|
466
|
+
mapset = grass.explicit_map_mapset(map) || grass.current_mapset
|
467
|
+
if grass.map_exists?(map, type: type, mapset: mapset)
|
468
|
+
grass.remove_map(map, type: type, mapset: mapset)
|
469
|
+
end
|
470
|
+
end
|
471
|
+
results.files.each do |file|
|
472
|
+
if File.exists?(file)
|
473
|
+
if File.directory?(file)
|
474
|
+
FileUtils.rm_rf file
|
475
|
+
else
|
476
|
+
FileUtils.rm file
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
end
|
481
|
+
|
482
|
+
end
|
483
|
+
end
|