rgeoserver 0.5.9 → 0.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. data/.gitignore +1 -1
  2. data/Gemfile +1 -7
  3. data/Gemfile.lock +147 -0
  4. data/README.rdoc +69 -78
  5. data/Rakefile +2 -1
  6. data/VERSION +1 -1
  7. data/bin/batch_load.rb +138 -0
  8. data/config/defaults.yml +17 -0
  9. data/examples/batch_demo.rb +170 -0
  10. data/examples/catalog_migration.rb +2 -2
  11. data/examples/cluster_demo.rb +1 -1
  12. data/examples/coverage_stores.rb +42 -0
  13. data/examples/demo_druid_workspace.rb +6 -0
  14. data/examples/deploy_dor_layer.rb +19 -0
  15. data/examples/sql_layer_demo.rb +1 -1
  16. data/lib/rgeoserver/catalog.rb +83 -86
  17. data/lib/rgeoserver/config.rb +11 -1
  18. data/lib/rgeoserver/coverage.rb +43 -15
  19. data/lib/rgeoserver/coveragestore.rb +46 -11
  20. data/lib/rgeoserver/datastore.rb +73 -62
  21. data/lib/rgeoserver/featuretype.rb +134 -71
  22. data/lib/rgeoserver/geoserver_url_helpers.rb +140 -10
  23. data/lib/rgeoserver/layer.rb +52 -16
  24. data/lib/rgeoserver/layergroup.rb +1 -2
  25. data/lib/rgeoserver/namespace.rb +1 -2
  26. data/lib/rgeoserver/resource.rb +37 -34
  27. data/lib/rgeoserver/rest_api_client.rb +33 -14
  28. data/lib/rgeoserver/style.rb +1 -2
  29. data/lib/rgeoserver/utils/boundingbox.rb +59 -16
  30. data/lib/rgeoserver/utils/metadata.rb +21 -0
  31. data/lib/rgeoserver/version.rb +1 -1
  32. data/lib/rgeoserver/wmsstore.rb +1 -6
  33. data/lib/rgeoserver/workspace.rb +23 -8
  34. data/lib/rgeoserver.rb +31 -38
  35. data/rgeoserver.gemspec +28 -20
  36. data/spec/fixtures/load_ex1.yml +45 -0
  37. data/spec/fixtures/load_ex2.yml +14 -0
  38. data/spec/functional/catalog_spec.rb +71 -0
  39. data/spec/functional/rest_api_client_spec.rb +215 -0
  40. data/spec/integration/geoserver_spec.rb +2 -2
  41. metadata +120 -66
  42. data/config/config_defaults.yml +0 -10
  43. data/examples/batch_example.rb +0 -105
@@ -1,21 +1,41 @@
1
1
 
2
2
  module RGeoServer
3
- # A feature type is a vector based spatial resource or data set that originates from a data store. In some cases, like Shapefile, a feature type has a one-to-one relationship with its data store. In other cases, like PostGIS, the relationship of feature type to data store is many-to-one, with each feature type corresponding to a table in the database.
4
- class FeatureType < ResourceInfo
5
- OBJ_ATTRIBUTES = {:catalog => "catalog", :name => "name", :workspace => "workspace", :data_store => "data_store", :enabled => "enabled", :metadata_links => "metadataLinks", :title => "title", :abstract => "abstract", :native_bounds => 'native_bounds', :latlon_bounds => "latlon_bounds", :projection_policy => 'projection_policy'}
6
- OBJ_DEFAULT_ATTRIBUTES =
7
- {
3
+ # A feature type is a vector based spatial resource or data set that originates from a data store.
4
+ # In some cases, like Shapefile, a feature type has a one-to-one relationship with its data store.
5
+ # In other cases, like PostGIS, the relationship of feature type to data store is many-to-one, with
6
+ # each feature type corresponding to a table in the database.
7
+ class FeatureType < ResourceInfo
8
+ OBJ_ATTRIBUTES = {
9
+ :catalog => "catalog",
10
+ :name => "name",
11
+ :native_name => "nativeName",
12
+ :workspace => "workspace",
13
+ :data_store => "data_store",
14
+ :enabled => "enabled",
15
+ :metadata => "metadata",
16
+ :metadata_links => "metadataLinks",
17
+ :title => "title",
18
+ :abstract => "abstract",
19
+ :keywords => 'keywords',
20
+ :native_bounds => 'native_bounds',
21
+ :latlon_bounds => "latlon_bounds",
22
+ :projection_policy => 'projection_policy'
23
+ }
24
+ OBJ_DEFAULT_ATTRIBUTES = {
8
25
  :catalog => nil,
9
26
  :workspace => nil,
10
27
  :data_store => nil,
11
28
  :name => nil,
29
+ :native_name => nil,
12
30
  :enabled => "false",
13
- :metadata_links => [],
31
+ :metadata => {},
32
+ :metadata_links => {},
14
33
  :title => nil,
15
34
  :abstract => nil,
35
+ :keywords => [],
16
36
  :native_bounds => {'minx'=>nil, 'miny' =>nil, 'maxx'=>nil, 'maxy'=>nil, 'crs' =>nil},
17
37
  :latlon_bounds => {'minx'=>nil, 'miny' =>nil, 'maxx'=>nil, 'maxy'=>nil, 'crs' =>nil},
18
- :projection_policy => :force
38
+ :projection_policy => :keep
19
39
  }
20
40
 
21
41
  define_attribute_methods OBJ_ATTRIBUTES.keys
@@ -25,6 +45,19 @@ module RGeoServer
25
45
  @@root = "featureTypes"
26
46
  @@resource_name = "featureType"
27
47
 
48
+ # see http://inspire.ec.europa.eu/schemas/common/1.0/common.xsd
49
+ METADATA_TYPES = {
50
+ 'ISO19139' => 'text/xml',
51
+ 'TC211' => 'text/xml'
52
+ }
53
+
54
+ # see https://github.com/geoserver/geoserver/blob/master/src/main/src/main/java/org/geoserver/catalog/ProjectionPolicy.java
55
+ PROJECTION_POLICIES = {
56
+ :force => 'FORCE_DECLARED',
57
+ :reproject => 'REPROJECT_TO_DECLARED',
58
+ :keep => 'NONE'
59
+ }
60
+
28
61
  def self.root
29
62
  @@root
30
63
  end
@@ -42,20 +75,45 @@ module RGeoServer
42
75
  end
43
76
 
44
77
  def route
78
+ raise GeoServerArgumentError, "workspace not defined" unless @workspace
79
+ raise GeoServerArgumentError, "data_store not defined" unless @data_store
45
80
  @@route % [@workspace.name , @data_store.name]
46
81
  end
82
+
83
+ def to_mimetype(type, default = 'text/xml')
84
+ k = type.to_s.strip.upcase
85
+ return METADATA_TYPES[k] if METADATA_TYPES.include? k
86
+ default
87
+ end
47
88
 
48
89
  def message
49
90
  builder = Nokogiri::XML::Builder.new do |xml|
50
91
  xml.featureType {
51
- xml.name @name if new?
52
- xml.enabled @enabled if (enabled_changed? || new?)
53
- xml.title title
54
- xml.abstract abstract
92
+ xml.nativeName @native_name.nil?? @name : @native_name if new? # on new only
93
+ xml.name @name
94
+ xml.enabled @enabled
95
+ xml.title @title
96
+ xml.abstract @abstract
97
+ xml.keywords {
98
+ @keywords.compact.uniq.each do |k|
99
+ xml.string RGeoServer::Metadata::to_keyword(k)
100
+ end
101
+ } unless @keywords.empty?
55
102
 
103
+ xml.metadataLinks {
104
+ @metadata_links.each do |m|
105
+ raise ArgumentError, "Malformed metadata_links" unless m.is_a? Hash
106
+ xml.metadataLink {
107
+ xml.type_ to_mimetype(m['metadataType'])
108
+ xml.metadataType m['metadataType']
109
+ xml.content m['content']
110
+ }
111
+ end
112
+ } unless @metadata_links.empty?
113
+
56
114
  xml.store(:class => 'dataStore') {
57
115
  xml.name @data_store.name
58
- } if new? || data_store_changed?
116
+ } if new? or data_store_changed?
59
117
 
60
118
  xml.nativeBoundingBox {
61
119
  xml.minx native_bounds['minx'] if native_bounds['minx']
@@ -63,7 +121,7 @@ module RGeoServer
63
121
  xml.maxx native_bounds['maxx'] if native_bounds['maxx']
64
122
  xml.maxy native_bounds['maxy'] if native_bounds['maxy']
65
123
  xml.crs native_bounds['crs'] if native_bounds['crs']
66
- } if valid_native_bounds?
124
+ } if valid_native_bounds? and (new? or native_bounds_changed?)
67
125
 
68
126
  xml.latLonBoundingBox {
69
127
  xml.minx latlon_bounds['minx'] if latlon_bounds['minx']
@@ -71,11 +129,11 @@ module RGeoServer
71
129
  xml.maxx latlon_bounds['maxx'] if latlon_bounds['maxx']
72
130
  xml.maxy latlon_bounds['maxy'] if latlon_bounds['maxy']
73
131
  xml.crs latlon_bounds['crs'] if latlon_bounds['crs']
74
- } if valid_latlon_bounds?
132
+ } if valid_latlon_bounds? and (new? or latlon_bounds_changed?)
75
133
 
76
- xml.projectionPolicy get_projection_policy_message(projection_policy) if projection_policy
134
+ xml.projectionPolicy get_projection_policy_message(projection_policy) if projection_policy and new? or projection_policy_changed?
77
135
 
78
- if new?
136
+ if new? # XXX: hard coded attributes
79
137
  xml.attributes {
80
138
  xml.attribute {
81
139
  xml.name 'the_geom'
@@ -85,44 +143,38 @@ module RGeoServer
85
143
  xml.binding 'com.vividsolutions.jts.geom.Point'
86
144
  }
87
145
  }
88
- else
89
- xml.metadataLinks {
90
- @metadata_links.each{ |m|
91
- xml.metadataLink {
92
- xml.type_ m['type']
93
- xml.metadataType m['metadataType']
94
- xml.content m['content']
95
- }
96
- }
97
- } if metadata_links_changed?
98
146
  end
99
147
  }
100
148
  end
101
149
  @message = builder.doc.to_xml
150
+ ap({:message => @message}) if $DEBUG
151
+ @message
102
152
  end
103
153
 
104
154
 
105
155
  # @param [RGeoServer::Catalog] catalog
106
156
  # @param [Hash] options
107
157
  def initialize catalog, options
108
- super({})
158
+ raise GeoServerArgumentError, "FeatureType.new requires :data_store option" unless options.include?(:data_store)
159
+ raise GeoServerArgumentError, "FeatureType.new requires :name option" unless options.include?(:name)
160
+ super(catalog)
109
161
  _run_initialize_callbacks do
110
- @catalog = catalog
111
162
  workspace = options[:workspace] || 'default'
112
163
  if workspace.instance_of? String
113
164
  @workspace = @catalog.get_workspace(workspace)
114
165
  elsif workspace.instance_of? Workspace
115
166
  @workspace = workspace
116
167
  else
117
- raise "Not a valid workspace"
168
+ raise GeoServerArgumentError, "Not a valid workspace: #{workspace}"
118
169
  end
170
+
119
171
  data_store = options[:data_store]
120
172
  if data_store.instance_of? String
121
- @data_store = DataStore.new @catalog, :workspace => @workspace, :name => data_store
173
+ @data_store = @catalog.get_data_store(@workspace.name, data_store)
122
174
  elsif data_store.instance_of? DataStore
123
175
  @data_store = data_store
124
176
  else
125
- raise "Not a valid data store"
177
+ raise GeoServerArgumentError, "Not a valid datastore: #{data_store}"
126
178
  end
127
179
 
128
180
  @name = options[:name].strip
@@ -132,77 +184,88 @@ module RGeoServer
132
184
 
133
185
  def profile_xml_to_hash profile_xml
134
186
  doc = profile_xml_to_ng profile_xml
187
+ ft = doc.at_xpath('//' + FeatureType::resource_name)
188
+ ap({:doc => doc, :ft => ft}) if $DEBUG
135
189
  h = {
136
- "name" => doc.at_xpath('//name').text.strip,
137
- "title" => doc.at_xpath('//title/text()').to_s,
138
- "abstract" => doc.at_xpath('//abstract/text()').to_s,
190
+ "name" => ft.at_xpath('name').text,
191
+ "native_name" => ft.at_xpath('nativeName').text,
192
+ "title" => ft.at_xpath('title').text,
193
+ "abstract" => ft.at_xpath('abstract/text()'), # optional
194
+ "keywords" => ft.xpath('keywords/string').collect { |k| k.at_xpath('.').text},
139
195
  "workspace" => @workspace.name,
140
196
  "data_store" => @data_store.name,
141
- "nativeName" => doc.at_xpath('//nativeName/text()').to_s,
142
- "srs" => doc.at_xpath('//srs/text()').to_s,
197
+ "srs" => ft.at_xpath('srs').text,
143
198
  "native_bounds" => {
144
- 'minx' => doc.at_xpath('//nativeBoundingBox/minx/text()').to_s.to_f,
145
- 'miny' => doc.at_xpath('//nativeBoundingBox/miny/text()').to_s.to_f,
146
- 'maxx' => doc.at_xpath('//nativeBoundingBox/maxx/text()').to_s.to_f,
147
- 'maxy' => doc.at_xpath('//nativeBoundingBox/maxy/text()').to_s.to_f,
148
- 'crs' => doc.at_xpath('//nativeBoundingBox/crs/text()').to_s
199
+ 'minx' => ft.at_xpath('nativeBoundingBox/minx').text.to_f,
200
+ 'miny' => ft.at_xpath('nativeBoundingBox/miny').text.to_f,
201
+ 'maxx' => ft.at_xpath('nativeBoundingBox/maxx').text.to_f,
202
+ 'maxy' => ft.at_xpath('nativeBoundingBox/maxy').text.to_f,
203
+ 'crs' => ft.at_xpath('srs').text
149
204
  },
150
205
  "latlon_bounds" => {
151
- 'minx' => doc.at_xpath('//latLonBoundingBox/minx/text()').to_s.to_f,
152
- 'miny' => doc.at_xpath('//latLonBoundingBox/miny/text()').to_s.to_f,
153
- 'maxx' => doc.at_xpath('//latLonBoundingBox/maxx/text()').to_s.to_f,
154
- 'maxy' => doc.at_xpath('//latLonBoundingBox/maxy/text()').to_s.to_f,
155
- 'crs' => doc.at_xpath('//latLonBoundingBox/crs/text()').to_s
206
+ 'minx' => ft.at_xpath('latLonBoundingBox/minx').text.to_f,
207
+ 'miny' => ft.at_xpath('latLonBoundingBox/miny').text.to_f,
208
+ 'maxx' => ft.at_xpath('latLonBoundingBox/maxx').text.to_f,
209
+ 'maxy' => ft.at_xpath('latLonBoundingBox/maxy').text.to_f,
210
+ 'crs' => ft.at_xpath('latLonBoundingBox/crs').text
156
211
  },
157
- "projection_policy" => get_projection_policy_sym(doc.at_xpath('//projectionPolicy').text.strip),
158
- "metadataLinks" => doc.xpath('//metadataLinks/metadataLink').collect{ |m|
212
+ "projection_policy" => get_projection_policy_sym(ft.at_xpath('projectionPolicy').text),
213
+ "metadata_links" => ft.xpath('metadataLinks/metadataLink').collect{ |m|
159
214
  {
160
- 'type' => m.at_xpath('//type/text()').to_s,
161
- 'metadataType' => m.at_xpath('//metadataType/text()').to_s,
162
- 'content' => m.at_xpath('//content/text()').to_s
215
+ 'type' => m.at_xpath('type').text,
216
+ 'metadataType' => m.at_xpath('metadataType').text,
217
+ 'content' => m.at_xpath('content').text
163
218
  }
164
219
  },
165
- "attributes" => doc.xpath('//attributes/attribute').collect{ |a|
220
+ "attributes" => ft.xpath('attributes/attribute').collect{ |a|
166
221
  {
167
- 'name' => a.at_xpath('//name/text()').to_s,
168
- 'minOccurs' => a.at_xpath('//minOccurs/text()').to_s,
169
- 'maxOccurs' => a.at_xpath('//maxOccurs/text()').to_s,
170
- 'nillable' => a.at_xpath('//nillable/text()').to_s,
171
- 'binding' => a.at_xpath('//binding/text()').to_s
222
+ 'name' => a.at_xpath('name').text,
223
+ 'minOccurs' => a.at_xpath('minOccurs').text,
224
+ 'maxOccurs' => a.at_xpath('maxOccurs').text,
225
+ 'nillable' => a.at_xpath('nillable').text,
226
+ 'binding' => a.at_xpath('binding').text
172
227
  }
173
228
  }
174
229
  }.freeze
230
+ ap({:h => h}) if $DEBUG
175
231
  h
176
232
  end
177
233
 
178
234
  def valid_native_bounds?
179
- native_bounds &&
180
- ![native_bounds['minx'], native_bounds['miny'], native_bounds['maxx'], native_bounds['maxy'], native_bounds['crs']].compact.empty?
235
+ bbox = RGeoServer::BoundingBox.new(native_bounds)
236
+ ap bbox if $DEBUG
237
+ not bbox.nil? and bbox.valid? and not native_bounds['crs'].empty?
181
238
  end
182
239
 
183
240
  def valid_latlon_bounds?
184
- latlon_bounds &&
185
- ![latlon_bounds['minx'], latlon_bounds['miny'], latlon_bounds['maxx'], latlon_bounds['maxy'], latlon_bounds['crs']].compact.empty?
241
+ bbox = RGeoServer::BoundingBox.new(latlon_bounds)
242
+ ap bbox if $DEBUG
243
+ not bbox.nil? and bbox.valid? and not latlon_bounds['crs'].empty?
244
+ end
245
+
246
+ def update_params name_route = name
247
+ super(name_route)
248
+ # recalculate='nativebbox,latlonbbox'
186
249
  end
187
250
 
188
251
  private
252
+
189
253
  def get_projection_policy_sym value
190
- case value.upcase
191
- when 'FORCE_DECLARED' then :force
192
- when 'REPROJECT_TO_DECLARED' then :reproject
193
- when 'NONE' then :keep
254
+ v = value.strip.upcase
255
+ if PROJECTION_POLICIES.has_value? v
256
+ PROJECTION_POLICIES.invert[v]
194
257
  else
195
- raise ArgumentError, "There is not correspondent to '%s'" % value
258
+ raise GeoServerArgumentError, "Invalid PROJECTION_POLICY: #{v}"
196
259
  end
197
260
  end
198
261
 
199
262
  def get_projection_policy_message value
200
- case value
201
- when :force then 'FORCE_DECLARED'
202
- when :reproject then 'REPROJECT_TO_DECLARED'
203
- when :keep then 'NONE'
263
+ k = value
264
+ k = value.strip.to_sym if not value.is_a? Symbol
265
+ if PROJECTION_POLICIES.has_key? k
266
+ PROJECTION_POLICIES[k]
204
267
  else
205
- raise ArgumentError, "There is not correspondent to '%s'" % value
268
+ raise GeoServerArgumentError, "Invalid PROJECTION_POLICY: #{k}"
206
269
  end
207
270
  end
208
271
  end
@@ -1,17 +1,147 @@
1
1
 
2
2
  module RGeoServer
3
3
  module GeoServerUrlHelpers
4
- API_DOCUMENTATION = "http://docs.geoserver.org/latest/en/user/restconfig/rest-config-api.html"
5
-
6
- def url_for base, options = nil
7
- base = { base => nil } unless base.is_a? Hash
8
- format = options.delete(:format) || 'xml'
9
- new_base = base.map{ |key,value| value.nil?? key.to_s : [key.to_s, CGI::escape(value.to_s)].join("/") }.join("/")
10
- new_base = new_base.gsub(/\/$/,'')
11
- new_base += ".#{format}"
12
- "#{new_base}" + (("?#{options.map { |key, value| "#{CGI::escape(key.to_s)}=#{CGI::escape(value.to_s)}"}.join("&") }" if options and not options.empty?) || '')
13
-
4
+
5
+ # Valid URI sequences for REST API
6
+ # See http://docs.geoserver.org/stable/en/user/rest/index.html
7
+ URI_SEQUENCES = [
8
+ %w{settings},
9
+ %w{settings contact},
10
+ %w{workspaces},
11
+ %w{workspaces settings},
12
+ %w{namespaces},
13
+ %w{workspaces datastores},
14
+ %w{workspaces datastores file},
15
+ %w{workspaces datastores external},
16
+ %w{workspaces datastores url},
17
+ %w{workspaces datastores featuretypes},
18
+ %w{workspaces coveragestores},
19
+ %w{workspaces coveragestores file},
20
+ %w{workspaces coveragestores coverages},
21
+ %w{styles},
22
+ %w{workspaces styles},
23
+ %w{layers},
24
+ %w{layers styles},
25
+ %w{layergroups},
26
+ %w{workspaces layergroups},
27
+ %w{fonts},
28
+ %w{templates},
29
+ %w{services wcs settings},
30
+ %w{services wfs settings},
31
+ %w{services wms settings},
32
+ %w{reload},
33
+ %w{reset},
34
+ %w{about}
35
+ ].map {|x| x.map(&:to_sym)}
36
+
37
+ # Regexp processing for #URI_SEQUENCES
38
+ URI_REGEX = URI_SEQUENCES.map do |a|
39
+ Regexp.new('^' + a.map {|x| x.to_s}.join('/\w+/') + '[/\w]*$')
14
40
  end
41
+
42
+ # Valid formats for REST API
43
+ # See http://docs.geoserver.org/stable/en/user/rest/api/details.html
44
+ URI_FORMATS = %w{xml json html sld zip}.map(&:to_sym)
45
+
46
+ # See http://docs.geoserver.org/latest/en/user/rest/api/
47
+ # @param [Hash] base, examples:
48
+ # - { :workspaces => nil }
49
+ #
50
+ # @param [Hash] options
51
+ # @return [String] baseURL for REST API, e.g.,:
52
+ # - settings.xml
53
+ # - layers/_name_.xml
54
+ # - styles/_name_.xml
55
+ # - workspaces/_name_.xml
56
+ # - workspaces/_name_/settings.xml
57
+ # - namespaces/_name_.xml
58
+ # - workspaces/_name_/datastores/_name_.xml
59
+ # - workspaces/_name_/datastores/_name_/featuretype/_name_.xml
60
+ def url_for base, options = {}
61
+ raise GeoServerArgumentError, "options must be Hash" unless options.is_a? Hash
62
+ if base.is_a? String
63
+ $stderr.puts "WARNING: deprecated usage -- base should be Hash"
64
+ base = { base.to_sym => nil } if base.is_a? String
65
+ end
15
66
 
67
+ base = Hash[base.map {|k,v| [k.to_sym, v]}] unless base.keys.select {|k| not k.is_a? Symbol}.size == 0
68
+
69
+ format = (options.delete(:format) if options.include?(:format)) || :xml
70
+ raise GeoServerArgumentError, "Unknown REST API format: '#{format}'" unless URI_FORMATS.include?(format)
71
+
72
+ new_base = base.collect {|k,v| v.nil?? "#{k}" : "#{k}/#{v}"}.join('/').to_s
73
+ new_base = new_base.gsub(%r{/$}, '')
74
+
75
+ raise GeoServerArgumentError, "Invalid REST URI syntax: #{new_base} from #{base}" unless URI_REGEX.each.select {|r| r.match(new_base)}.size > 0
76
+
77
+ new_base += ".#{format}"
78
+ if not options.empty?
79
+ new_base += "?" + options.collect {|k,v| [CGI::escape(k.to_s), CGI::escape(v.to_s)].join('=')}.join('&')
80
+ end
81
+ ap "url_for: #{base} #{options} => #{new_base}" if $DEBUG
82
+ new_base
83
+ end
84
+
85
+ # See http://docs.geoserver.org/latest/en/user/rest/api/
86
+ # @param [Hash] sequence with values. Note that the sequence can end in a nil, but none of the preceding elements may be nil
87
+ # - 'workspaces'
88
+ # - { :workspaces => nil }
89
+ # - { :workspaces => 'default', :datastores => nil }
90
+ # - { :about => 'version' }
91
+ #
92
+ # @param [Hash] options :format. See `URI_FORMATS`
93
+ #
94
+ # @return [String] baseURL for REST API, e.g.,:
95
+ # - workspaces.xml
96
+ # - workspaces/_name_.json
97
+ # - workspaces/_name_/datastores.xml
98
+ # - workspaces/_name_/datastores/_name_/featuretype/_name_.xml
99
+ #
100
+ # @raise [GeoServerArgumentError] if sequence is invalid
101
+ # def url_for_NOT_READY sequence, options = { }
102
+ # if sequence.is_a? Hash
103
+ # # ap "url_for(String): #{sequence}"
104
+ # new_base = sequence
105
+ # else
106
+ # # ap "url_for(Hash): #{sequence}"
107
+ # raise GeoServerArgumentError, "sequence must be Hash" unless sequence.is_a? Hash
108
+ # if sequence.keys.select {|k| k.is_a? Symbol}.empty?
109
+ # raise GeoServerArgumentError, "Sequence must use symbol keys: #{sequence}"
110
+ # end
111
+ #
112
+ # raise GeoServerArgumentError, "Invalid sequence: #{sequence}" unless URI_SEQUENCES.include?(sequence.keys.to_a)
113
+ #
114
+ # # sequence can end in a nil, but none of the preceding elements may
115
+ # if sequence.size > 1
116
+ # sequence.keys.slice(0..-2).reverse_each do |k|
117
+ # raise GeoServerArgumentError, "Missing required sequence value for #{k}: #{sequence}" if sequence[k] == nil
118
+ # end
119
+ # end
120
+ #
121
+ # # `about` sequence can only be :version or :manifest
122
+ # if sequence.include?(:about) and not [:manifest, :version].include?(sequence[:about])
123
+ # raise GeoServerArgumentError, "Missing required sequence symbol for about [:manifest, :version]: #{sequence}"
124
+ # end
125
+ #
126
+ # if sequence.keys == [:layers, :styles]
127
+ # raise GeoServerArgumentError, "Cannot retrieve specific layer style: #{sequence}" if sequence[:styles] != nil
128
+ # end
129
+ #
130
+ # raise GeoServerArgumentError, "options must be Hash: #{options}" unless options.is_a? Hash
131
+ # format = options.delete(:format) || :xml
132
+ # raise GeoServerArgumentError, "Unknown REST API format: '#{format}'" unless URI_FORMATS.include?(format)
133
+ #
134
+ # new_base = sequence.collect {|k,v| v.nil?? "#{k}" : "#{k}/#{v}"}.join('/')
135
+ # end
136
+ #
137
+ # raise TypeError if new_base.is_a? String if $DEBUG
138
+ #
139
+ # new_base = new_base.gsub(/\/$/,'')
140
+ # new_base += ".#{format}"
141
+ # if not options.empty?
142
+ # new_base += "?" + CGI::escape(options.collect {|k,v| "#{k}=#{v}"}.join('&'))
143
+ # end
144
+ # new_base
145
+ # end
16
146
  end
17
147
  end
@@ -98,14 +98,22 @@ module RGeoServer
98
98
 
99
99
  # @param [RGeoServer::Catalog] catalog
100
100
  # @param [Hash] options
101
- # @option options [String] :name
101
+ # @option options [String] :name required
102
102
  # @option options [String] :default_style
103
103
  # @option options [Array<String>] :alternate_styles
104
104
  def initialize catalog, options
105
- super({})
105
+ super(catalog)
106
106
  _run_initialize_callbacks do
107
- @catalog = catalog
108
- @name = options[:name].strip
107
+ if options[:name].instance_of? Layer
108
+ options[:name] = options[:name].name.to_s
109
+ end
110
+
111
+ raise GeoServerArgumentError, "Layer requires :name option" unless options.include? :name and options[:name].is_a? String
112
+ @name = options[:name].to_s.strip
113
+ ap({:init_name => @name}) if $DEBUG
114
+
115
+ raise NotImplementedError, ":default_style" if options.include? :default_style
116
+ raise NotImplementedError, ":alternate_styles" if options.include? :alternate_styles
109
117
  #@default_style = options[:default_style] || ''
110
118
  #@alternate_styles = options[:alternate_styles] || []
111
119
  end
@@ -116,7 +124,7 @@ module RGeoServer
116
124
  if r.is_a?(RGeoServer::Coverage) || r.is_a?(RGeoServer::FeatureType)
117
125
  @resource = r
118
126
  else
119
- raise 'Unknown resource type'
127
+ raise GeoServerArgumentError, 'Unknown resource type: #{r.class}'
120
128
  end
121
129
  end
122
130
 
@@ -132,9 +140,17 @@ module RGeoServer
132
140
  when 'coverage'
133
141
  return RGeoServer::Coverage.new @catalog, :workspace => workspace, :coverage_store => store, :name => name
134
142
  when 'featureType'
135
- return RGeoServer::FeatureType.new @catalog, :workspace => workspace, :data_store => store, :name => name
143
+ ap({:catalog => @catalog, :workspace => workspace, :data_store => store, :name => name}) if $DEBUG
144
+ begin
145
+ ft = RGeoServer::FeatureType.new @catalog, :workspace => workspace, :data_store => store, :name => name
146
+ ap({:featureType => ft, :route => ft.route}) if $DEBUG
147
+ rescue Exception => e
148
+ ap({:errormsg => "#{e}", :error => e, :trace => e.backtrace}) if $DEBUG
149
+ end
150
+
151
+ return ft
136
152
  else
137
- raise 'Unknown resource type'
153
+ raise GeoServerArgumentError, 'Unknown resource type: #{data_type}'
138
154
  end
139
155
  else
140
156
  nil
@@ -146,11 +162,11 @@ module RGeoServer
146
162
 
147
163
  # TODO: Simplify if necessary with "/layers/<l>/styles[.<format>]", as specified in the API
148
164
  def get_default_style &block
149
- self.class.list Style, @catalog, @default_style, {:layer => self}, check_remote = false, &block
165
+ self.class.list Style, @catalog, @default_style, {:layer => self}, false, &block
150
166
  end
151
167
 
152
168
  def get_alternate_styles &block
153
- self.class.list Style, @catalog, @alternate_styles, {:layer => self}, check_remote = false, &block
169
+ self.class.list Style, @catalog, @alternate_styles, {:layer => self}, false, &block
154
170
  end
155
171
 
156
172
  def profile_xml_to_hash profile_xml
@@ -179,7 +195,7 @@ module RGeoServer
179
195
  "store" => store,
180
196
  "workspace" => workspace
181
197
  },
182
- "metadata" => doc.xpath('//metadata/entry').inject({}){ |h, e| h.merge(e['key']=> e.text.to_s) }
198
+ "metadata" => doc.xpath('//metadata/entry').inject({}){ |h2, e| h2.merge(e['key']=> e.text.to_s) }
183
199
  }.freeze
184
200
  h
185
201
  end
@@ -188,6 +204,12 @@ module RGeoServer
188
204
  resource.workspace
189
205
  end
190
206
 
207
+ # Return full name of resource with namespace prefix
208
+ def prefixed_name
209
+ return "#{workspace.name}:#{name}" if self.respond_to?(:workspace)
210
+ raise "Workspace is not defined for this resource"
211
+ end
212
+
191
213
  #= GeoWebCache Operations for this layer
192
214
  # See http://geowebcache.org/docs/current/rest/seed.html
193
215
  # See RGeoServer::Catalog.seed_terminate for stopping pending and/or running tasks for any layer
@@ -202,6 +224,8 @@ module RGeoServer
202
224
  # :threadCount => 1
203
225
  # }
204
226
  # > lyr.seed :issue, options
227
+ #
228
+ # @see http://geowebcache.org/docs/current/rest/
205
229
 
206
230
  # @param[String] operation
207
231
  # @option operation[Symbol] :issue seed
@@ -213,16 +237,25 @@ module RGeoServer
213
237
  sub_path = "seed/#{prefixed_name}.xml"
214
238
  case op
215
239
  when :issue
216
- @catalog.do_url sub_path, :post, build_seed_request(:seed, options), {}, @catalog.gwc_client
240
+ @catalog.do_url sub_path, :post, _build_seed_request(:seed, options), {}, @catalog.gwc_client
217
241
  when :truncate
218
- @catalog.do_url sub_path, :post, build_seed_request(:truncate, options), {}, @catalog.gwc_client
242
+ @catalog.do_url sub_path, :post, _build_seed_request(:truncate, options), {}, @catalog.gwc_client
219
243
  when :status
220
- raise NotImplementedError
244
+ raise NotImplementedError, "#{op}"
221
245
  end
222
246
  end
223
247
 
224
- # @param[Hash] options for seed message
225
- def build_seed_request operation, options
248
+ private
249
+ # @param[Hash] options for seed message, requiring
250
+ # options[:srs][:number]
251
+ # options[:bounds][:coords]
252
+ # options[:gridSetId]
253
+ # options[:zoomStart]
254
+ # options[:zoomStop]
255
+ # options[:gridSetId]
256
+ # options[:gridSetId]
257
+ #
258
+ def _build_seed_request operation, options
226
259
  builder = Nokogiri::XML::Builder.new do |xml|
227
260
  xml.seedRequest {
228
261
  xml.name prefixed_name
@@ -241,9 +274,11 @@ module RGeoServer
241
274
 
242
275
  xml.type_ operation
243
276
 
244
- [:gridSetId, :zoomStart, :zoomStop, :format, :threadCount].each { |p|
277
+ [:gridSetId, :zoomStart, :zoomStop, :threadCount].each { |p|
245
278
  eval "xml.#{p.to_s} options[p]" unless options[p].nil?
246
279
  }
280
+
281
+ xml.format_ options[:tileFormat] unless options[:tileFormat].nil?
247
282
 
248
283
  xml.parameters {
249
284
  options[:parameters].each_pair { |k,v|
@@ -255,6 +290,7 @@ module RGeoServer
255
290
  } if options[:parameters].is_a?(Hash)
256
291
  }
257
292
  end
293
+ ap({:build_seed_request => builder.doc}) if $DEBUG
258
294
  return builder.doc.to_xml
259
295
  end
260
296
  end
@@ -68,9 +68,8 @@ module RGeoServer
68
68
  # @param [Hash] options
69
69
  # @option options [String] :name
70
70
  def initialize catalog, options
71
- super({})
71
+ super(catalog)
72
72
  _run_initialize_callbacks do
73
- @catalog = catalog
74
73
  @name = options[:name].strip
75
74
  @workspace = options[:workspace]
76
75
  end
@@ -41,9 +41,8 @@ module RGeoServer
41
41
  # @param [RGeoServer::Catalog] catalog
42
42
  # @param [Hash] options
43
43
  def initialize catalog, options
44
- super({})
44
+ super(catalog)
45
45
  _run_initialize_callbacks do
46
- @catalog = catalog
47
46
  @name = options[:name].strip
48
47
  @uri = options[:uri] if options[:uri]
49
48
  end