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.
@@ -1,4 +1,4 @@
1
- module GrassGis
2
- class Error < StandardError
3
- end
4
- end
1
+ module GrassGis
2
+ class Error < StandardError
3
+ end
4
+ end
@@ -1,53 +1,53 @@
1
- module GrassGis
2
- class Location
3
- def initialize(context)
4
- @context = context
5
- @configuration = @context.configuration
6
- @gisdbase = @configuration[:gisdbase]
7
- @location = @configuration[:location]
8
- @path = File.join(@gisdbase, @location)
9
- end
10
-
11
- attr_reader :path
12
-
13
- def to_s
14
- @location
15
- end
16
-
17
- def exists?
18
- File.directory?(@path)
19
- end
20
-
21
- def create!(options = {})
22
- raise Error, "Location #{@location} already exists" if exists?
23
- raise Error, "A file with the same name #{@location} exists" if File.exists?(@path)
24
- raise Error, "GRASSDBASE doesn't exist" unless File.directory?(@gisdbase)
25
- epsg = options[:epsg]
26
- limits = options[:limits]
27
- desc = options[:desc]
28
- raise Error, "An EPSG code is needed to define a new loaction" unless epsg
29
- @context.g.proj '-t', epsg: epsg, location: @location
30
- permanent = permanent_path
31
- if desc
32
- desc_file = File.join(permanent, 'MYNAME')
33
- File.write desc_file, desc
34
- end
35
- if limits
36
- w, s, e, n = limits
37
- res = options[:res]
38
- # @context.g.mapset mapset: 'PERMANENT', location: @location
39
- @context.change_mapset 'PERMANENT'
40
- @context.g.region w: w, s: s, e: e, n: n, res: res
41
- FileUtils.cp File.join(permanent, 'WIND'), File.join(permanent, 'DEFAULT_WIND')
42
- end
43
- end
44
-
45
- def mapset_path(mapset)
46
- File.join(@path, mapset)
47
- end
48
-
49
- def permanent_path
50
- mapset_path('PERMANENT')
51
- end
52
- end
53
- end
1
+ module GrassGis
2
+ class Location
3
+ def initialize(context)
4
+ @context = context
5
+ @configuration = @context.configuration
6
+ @gisdbase = @configuration[:gisdbase]
7
+ @location = @configuration[:location]
8
+ @path = File.join(@gisdbase, @location)
9
+ end
10
+
11
+ attr_reader :path
12
+
13
+ def to_s
14
+ @location
15
+ end
16
+
17
+ def exists?
18
+ File.directory?(@path)
19
+ end
20
+
21
+ def create!(options = {})
22
+ raise Error, "Location #{@location} already exists" if exists?
23
+ raise Error, "A file with the same name #{@location} exists" if File.exists?(@path)
24
+ raise Error, "GRASSDBASE doesn't exist" unless File.directory?(@gisdbase)
25
+ epsg = options[:epsg]
26
+ limits = options[:limits]
27
+ desc = options[:desc]
28
+ raise Error, "An EPSG code is needed to define a new loaction" unless epsg
29
+ @context.g.proj '-t', epsg: epsg, location: @location
30
+ permanent = permanent_path
31
+ if desc
32
+ desc_file = File.join(permanent, 'MYNAME')
33
+ File.write desc_file, desc
34
+ end
35
+ if limits
36
+ w, s, e, n = limits
37
+ res = options[:res]
38
+ # @context.g.mapset mapset: 'PERMANENT', location: @location
39
+ @context.change_mapset 'PERMANENT'
40
+ @context.g.region w: w, s: s, e: e, n: n, res: res
41
+ FileUtils.cp File.join(permanent, 'WIND'), File.join(permanent, 'DEFAULT_WIND')
42
+ end
43
+ end
44
+
45
+ def mapset_path(mapset)
46
+ File.join(@path, mapset)
47
+ end
48
+
49
+ def permanent_path
50
+ mapset_path('PERMANENT')
51
+ end
52
+ end
53
+ end
@@ -1,31 +1,31 @@
1
- module GrassGis
2
- class Mapset
3
- def initialize(context)
4
- @context = context
5
- @configuration = @context.configuration
6
- @location = Location.new(@context)
7
- @mapset = @configuration[:mapset]
8
- @path = File.join(@location.path, @mapset)
9
- end
10
-
11
- attr_reader :path
12
-
13
- def to_s
14
- @mapset
15
- end
16
-
17
- def exists?
18
- File.directory?(@path)
19
- end
20
-
21
- def create!(options = {})
22
- raise Error, "Mapset #{@mapset} already exists" if exists?
23
- raise Error, "A file with the same name #{@mapset} exists" if File.exists?(@path)
24
- raise Error, "Location doesn't exist" unless @location.exists?
25
- # @context.g.mapet '-c', mapset: @mapset, location: @location.to_s, dbase: @configuration[:gisdbase]
26
- FileUtils.mkdir_p @path
27
- permanent = @location.permanent_path
28
- FileUtils.cp File.join(permanent, 'DEFAULT_WIND'), File.join(@path, 'WIND')
29
- end
30
- end
31
- end
1
+ module GrassGis
2
+ class Mapset
3
+ def initialize(context)
4
+ @context = context
5
+ @configuration = @context.configuration
6
+ @location = Location.new(@context)
7
+ @mapset = @configuration[:mapset]
8
+ @path = File.join(@location.path, @mapset)
9
+ end
10
+
11
+ attr_reader :path
12
+
13
+ def to_s
14
+ @mapset
15
+ end
16
+
17
+ def exists?
18
+ File.directory?(@path)
19
+ end
20
+
21
+ def create!(options = {})
22
+ raise Error, "Mapset #{@mapset} already exists" if exists?
23
+ raise Error, "A file with the same name #{@mapset} exists" if File.exists?(@path)
24
+ raise Error, "Location doesn't exist" unless @location.exists?
25
+ # @context.g.mapet '-c', mapset: @mapset, location: @location.to_s, dbase: @configuration[:gisdbase]
26
+ FileUtils.mkdir_p @path
27
+ permanent = @location.permanent_path
28
+ FileUtils.cp File.join(permanent, 'DEFAULT_WIND'), File.join(@path, 'WIND')
29
+ end
30
+ end
31
+ end
@@ -1,73 +1,73 @@
1
- module GrassGis
2
-
3
- # Generate and execute GRASS commands
4
- #
5
- # r = GrassGis::Module.new('r')
6
- # r.resamp.stats '-n', input: "map1@mapset1", output: "map2"
7
- #
8
- # To execute a command without arguments, +run+ must be invoked explicitly:
9
- #
10
- # g = GrassGis::Module.new('g')
11
- # g.region.run
12
- #
13
- class Module
14
- def initialize(id, options = {})
15
- @id = id.to_s
16
- @parent = options[:parent]
17
- @context = options[:context]
18
- end
19
-
20
- def name
21
- if @parent
22
- "#{@parent.name}.#{@id}"
23
- else
24
- @id
25
- end
26
- end
27
-
28
- # Executes the command (with given arguments)
29
- # returns a SysCmd object (with status, status_value, output, error_output methods)
30
- def run(*args)
31
- stdin = nil
32
- cmd = SysCmd.command name do
33
- args.each do |arg|
34
- case arg
35
- when Hash
36
- arg.each do |key, value|
37
- next if value.nil?
38
- case value
39
- when Array
40
- value = value*","
41
- when String
42
- if value.include?("\n")
43
- raise "Cannot pass multiple options through STDIN" if stdin
44
- stdin = Support.unindent(value)
45
- value = "-"
46
- input stdin
47
- end
48
- end
49
- option key.to_s, equal_value: value
50
- end
51
- else
52
- option arg
53
- end
54
- end
55
- end
56
- if @context
57
- @context.execute cmd
58
- end
59
- cmd
60
- end
61
-
62
- def method_missing(method, *args)
63
- m = Module.new(method, parent: self, context: @context)
64
- if args.size > 0
65
- m.run *args
66
- else
67
- m
68
- end
69
- end
70
-
71
- end
72
-
73
- end
1
+ module GrassGis
2
+
3
+ # Generate and execute GRASS commands
4
+ #
5
+ # r = GrassGis::Module.new('r')
6
+ # r.resamp.stats '-n', input: "map1@mapset1", output: "map2"
7
+ #
8
+ # To execute a command without arguments, +run+ must be invoked explicitly:
9
+ #
10
+ # g = GrassGis::Module.new('g')
11
+ # g.region.run
12
+ #
13
+ class Module
14
+ def initialize(id, options = {})
15
+ @id = id.to_s
16
+ @parent = options[:parent]
17
+ @context = options[:context]
18
+ end
19
+
20
+ def name
21
+ if @parent
22
+ "#{@parent.name}.#{@id}"
23
+ else
24
+ @id
25
+ end
26
+ end
27
+
28
+ # Executes the command (with given arguments)
29
+ # returns a SysCmd object (with status, status_value, output, error_output methods)
30
+ def run(*args)
31
+ stdin = nil
32
+ cmd = SysCmd.command name do
33
+ args.each do |arg|
34
+ case arg
35
+ when Hash
36
+ arg.each do |key, value|
37
+ next if value.nil?
38
+ case value
39
+ when Array
40
+ value = value*","
41
+ when String
42
+ if value.include?("\n")
43
+ raise "Cannot pass multiple options through STDIN" if stdin
44
+ stdin = Support.unindent(value)
45
+ value = "-"
46
+ input stdin
47
+ end
48
+ end
49
+ option key.to_s, equal_value: value
50
+ end
51
+ else
52
+ option arg
53
+ end
54
+ end
55
+ end
56
+ if @context
57
+ @context.execute cmd
58
+ end
59
+ cmd
60
+ end
61
+
62
+ def method_missing(method, *args)
63
+ m = Module.new(method, parent: self, context: @context)
64
+ if args.size > 0
65
+ m.run *args
66
+ else
67
+ m
68
+ end
69
+ end
70
+
71
+ end
72
+
73
+ end
@@ -1,26 +1,26 @@
1
- module GrassGis
2
- module Support
3
- module_function
4
-
5
- def unindent(text, indent = nil)
6
- text = text.gsub(/\t/, ' '*8)
7
- mx = text.scan(/^ *[^\n\r]/)
8
- .flatten
9
- .map{ |s| s[-1,1]==' ' ? nil : (s.size-1) }
10
- .compact.min
11
- if mx && mx>0
12
- text.gsub!(/^ {1,#{mx}}/, "")
13
- end
14
- lines = text.split(/\r?\n/)
15
- if lines.first.strip.empty? || lines.last.strip.empty?
16
- lines.shift while lines.first.strip.empty?
17
- lines.pop while lines.last.strip.empty?
18
- end
19
- if indent
20
- indent = ' ' * indent if indent.kind_of?(Numeric)
21
- lines = lines.map { |line| "#{indent}#{line}" }
22
- end
23
- lines.join("\n")
24
- end
25
- end
26
- end
1
+ module GrassGis
2
+ module Support
3
+ module_function
4
+
5
+ def unindent(text, indent = nil)
6
+ text = text.gsub(/\t/, ' '*8)
7
+ mx = text.scan(/^ *[^\n\r]/)
8
+ .flatten
9
+ .map{ |s| s[-1,1]==' ' ? nil : (s.size-1) }
10
+ .compact.min
11
+ if mx && mx>0
12
+ text.gsub!(/^ {1,#{mx}}/, "")
13
+ end
14
+ lines = text.split(/\r?\n/)
15
+ if lines.first.strip.empty? || lines.last.strip.empty?
16
+ lines.shift while lines.first.strip.empty?
17
+ lines.pop while lines.last.strip.empty?
18
+ end
19
+ if indent
20
+ indent = ' ' * indent if indent.kind_of?(Numeric)
21
+ lines = lines.map { |line| "#{indent}#{line}" }
22
+ end
23
+ lines.join("\n")
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,215 @@
1
+ module GrassGis
2
+ # Convenient shortcuts and tools for use in GRASS sessions
3
+ # Currently only implemented for GRASS 7
4
+ module Tools
5
+ def map_exists?(map, options = {})
6
+ types = Array(options[:type])
7
+ types = 'all' if types.empty?
8
+ mapsets = []
9
+ if map.include?('@')
10
+ map, mapset = map.split('@')
11
+ mapsets << mapset
12
+ end
13
+ mapsets += Array(options[:mapset])
14
+ if mapsets.empty?
15
+ g.list types.join(',')
16
+ else
17
+ g.list types.join(','), mapset: mapsets.join(',')
18
+ end
19
+ maps = output.split.map { |m| m.split('@').first }
20
+ maps.include?(map)
21
+ end
22
+
23
+ def raster_exists?(map, options = {})
24
+ map_exists? map, options.merge(type: 'rast')
25
+ end
26
+
27
+ def vector_exists?(map, options = {})
28
+ map_exists? map, options.merge(type: 'vect')
29
+ end
30
+
31
+ def shell_to_hash
32
+ Hash[output.lines.map{|l| l.strip.split('=')}]
33
+ end
34
+
35
+ def raster_info(map)
36
+ r.info '-g', map
37
+ shell_to_hash
38
+ end
39
+
40
+ def region_info
41
+ g.region '-m'
42
+ shell_to_hash
43
+ end
44
+
45
+ def raster_res(map)
46
+ info = raster_info(map)
47
+ info.values_at('ewres', 'nsres').map(&:to_i)
48
+ end
49
+
50
+ def region_res
51
+ info = region_info
52
+ info.values_at('ewres', 'nsres').map(&:to_i)
53
+ end
54
+
55
+ def current_mapset
56
+ g.mapsets '-p'
57
+ current_mapset = output.lines.to_a.last.split.first
58
+ end
59
+
60
+ def with_mapset(mapset)
61
+ current = current_mapset
62
+ if mapset != current
63
+ change_mapset mapset
64
+ end
65
+ yield self
66
+ if mapset != current
67
+ change_mapset current
68
+ end
69
+ end
70
+
71
+ def available_mapsets
72
+ g.mapsets '-l'
73
+ output.lines.to_a.last.split
74
+ end
75
+
76
+ def accessible_mapsets
77
+ g.mapsets '-p'
78
+ output.lines.to_a.last.split
79
+ end
80
+
81
+ def explicit_map_mapset(map)
82
+ if map.include?('@')
83
+ map.split('@').last
84
+ end
85
+ end
86
+
87
+ def map_mapset(map, options = {})
88
+ if map.include?('@')
89
+ map.split('@').last
90
+ else
91
+ type = options[:type]
92
+ raise "Must specify type of the map" unless type
93
+ accessible = accessible_mapsets
94
+ accessible.each do |mapset|
95
+ return mapset if map_exists?(map, type: type, mapset: mapset)
96
+ end
97
+ available = available_mapsets - accessible
98
+ available.each do |mapset|
99
+ return mapset if map_exists?(map, type: type, mapset: mapset)
100
+ end
101
+ nil
102
+ end
103
+ end
104
+
105
+ def remove_map(map, options = {})
106
+ type = options[:type]
107
+ raise "Must specify type to remove a map" unless type
108
+ mapset = explicit_map_mapset(map) || options[:mapset] || map_mapset(map, type: type)
109
+ raise "Map not found #{map} (#{type})" unless mapset
110
+ with_mapset(mapset) do
111
+ if grass_version >= GrassGis.version('7.0.0')
112
+ g.remove '-f', type: type, name: map
113
+ else
114
+ param = { vector: 'vect', raster: 'rast', raster_3d: 'rast3d' }[type.to_sym]
115
+ g.remove '-f', param => map
116
+ end
117
+ end
118
+ end
119
+
120
+ def copy_map(map, options = {})
121
+ type = options[:type]
122
+ raise "Must specify type to copy a map" unless type
123
+ if map.include?('@')
124
+ map, mapset = map.split('@').last
125
+ end
126
+ from_mapset = options[:from]
127
+ if from_mapset && from_mapset != mapset
128
+ raise "Inconsistent origin mapset"
129
+ end
130
+ from_mapset ||= mapset
131
+ unless from_mapset
132
+ from_mapset = map_mapset(map, type: options[:type])
133
+ end
134
+ to_mapset = options[:to]
135
+ unless from_mapset && to_mapset
136
+ raise "Must specify origin and destination mapsets"
137
+ end
138
+ if from_mapset != to_mapset
139
+ original_map = "#{map}@#{from_mapset}"
140
+ with_mapset to_mapset do
141
+ if grass_version >= GrassGis.version('7.0.0')
142
+ g.copy type => [original_map, map]
143
+ else
144
+ param = { vector: 'vect', raster: 'rast', raster_3d: 'rast3d' }[type.to_sym]
145
+ g.copy param => [original_map, map]
146
+ end
147
+ end
148
+ original_map
149
+ end
150
+ end
151
+
152
+ def create_mapset(mapset)
153
+ if true
154
+ # TODO: if mapset optional argument added to Mapset constructor:
155
+ # ms = GrassGis::Mapset.new(self, mapset)
156
+ keep_mapset = configuration[:mapset]
157
+ configuration[:mapset] = mapset
158
+ ms = GrassGis::Mapset.new(self)
159
+ ms.create! unless ms.exists?
160
+ configuration[:mapset] = keep_mapset
161
+ else
162
+ # This will fail under windows
163
+ current = current_mapset
164
+ g.mapset '-c',
165
+ mapset: mapset,
166
+ location: configuration[:location],
167
+ dbase: configuration[:gisdbase]
168
+ g.mapset current
169
+ end
170
+ end
171
+
172
+ def move_map(map, options = {})
173
+ original_map = copy_map(map, options)
174
+ remove_map original_map, type: options[:type]
175
+ end
176
+
177
+ def resamp_average(options = {})
178
+ input_raster = options[:input]
179
+ raise "Raster #{input_raster} not found" unless raster_exists?(input_raster)
180
+ input_res = raster_res(input_raster)
181
+
182
+ if options[:output_res]
183
+ output_res = options[:output_res]
184
+ unless output_res.is_a?(Array)
185
+ output_res = [output_res, output_res]
186
+ end
187
+ else
188
+ output_res = region_res
189
+ end
190
+
191
+ output_raster = options[:output]
192
+
193
+ if options[:direction]
194
+ unless raster_exists?("#{input_raster}_sin")
195
+ g.region ewres: input_res[0], nsres: input_res[1]
196
+ r.mapcalc "#{input_raster}_sin = sin(#{input_raster})"
197
+ end
198
+ unless raster_exists?("#{input_raster}_cos")
199
+ g.region ewres: input_res[0], nsres: input_res[1]
200
+ r.mapcalc "#{input_raster}_cos = cos(#{input_raster})"
201
+ end
202
+ g.region ewres: output_res[0], nsres: output_res[1]
203
+ r.resamp.stats '--overwrite', input: "#{input_raster}_cos", output: "#{output_raster}_cos"
204
+ r.resamp.stats '--overwrite', input: "#{input_raster}_sin", output: "#{output_raster}_sin"
205
+ r.mapcalc "#{output_raster} = atan(#{output_raster}_cos,#{output_raster}_sin)"
206
+ r.colors map: output_raster, raster: input_raster
207
+ g.remove '-f', type: 'raster', name: ["#{output_raster}_cos", "#{output_raster}_sin"]
208
+ else
209
+ g.region ewres: output_res[0], nsres: output_res[1]
210
+ r.resamp.stats input: input_raster, output: output_raster
211
+ r.colors map: output_raster, raster: input_raster
212
+ end
213
+ end
214
+ end
215
+ end