grassgis 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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