rgeoserver 0.5.8.2 → 0.5.9
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +4 -2
- data/README.rdoc +7 -3
- data/VERSION +1 -1
- data/lib/rgeoserver.rb +4 -1
- data/lib/rgeoserver/catalog.rb +84 -57
- data/lib/rgeoserver/datastore.rb +92 -18
- data/lib/rgeoserver/featuretype.rb +107 -35
- data/lib/rgeoserver/layer.rb +43 -43
- data/lib/rgeoserver/layergroup.rb +110 -35
- data/lib/rgeoserver/resource.rb +27 -27
- data/lib/rgeoserver/utils/boundingbox.rb +88 -0
- data/lib/rgeoserver/utils/shapefile_info.rb +89 -0
- data/rgeoserver.gemspec +5 -1
- data/spec/fixtures/datasets/vector/granules.zip +0 -0
- data/spec/integration/geoserver_spec.rb +170 -55
- data/spec/utils/boundingbox_spec.rb +77 -0
- data/spec/utils/shapefile_info_spec.rb +22 -0
- metadata +183 -29
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
-c
|
data/Gemfile
CHANGED
data/README.rdoc
CHANGED
@@ -10,7 +10,7 @@ It provides primitive Ruby model abstraction.
|
|
10
10
|
== Documentation
|
11
11
|
The GeoServer REST Configuration API Reference can be found here:
|
12
12
|
|
13
|
-
http://docs.geoserver.org/stable/en/user/
|
13
|
+
http://docs.geoserver.org/stable/en/user/rest/index.html
|
14
14
|
|
15
15
|
|
16
16
|
== Installation
|
@@ -162,10 +162,14 @@ result as the ResourceInfo#save method.
|
|
162
162
|
Inspired on the {Rubydora}[https://github.com/cbeer/rubydora] gem. Followed somewhat closely to {gsconfig.py}[https://github.com/dwins/gsconfig.py]
|
163
163
|
|
164
164
|
== Contributors
|
165
|
+
This package is supported and maintained by Stanford University Libraries.
|
165
166
|
|
167
|
+
Bess Sadler <bess@stanford.edu>
|
166
168
|
|
167
169
|
== License
|
168
170
|
|
169
|
-
Copyright (c) 2012
|
171
|
+
Copyright (c) 2012 Stanford University
|
170
172
|
|
171
|
-
|
173
|
+
Author: Renzo Sanchez-Silva <renzo.sanchez.silva@gmail.com>.
|
174
|
+
|
175
|
+
Licensed under the Apache License, Version 2.0
|
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.5.
|
1
|
+
0.5.9
|
data/lib/rgeoserver.rb
CHANGED
@@ -19,6 +19,9 @@ module RGeoServer
|
|
19
19
|
autoload :Layer, "rgeoserver/layer"
|
20
20
|
autoload :LayerGroup, "rgeoserver/layergroup"
|
21
21
|
|
22
|
+
autoload :BoundingBox, "rgeoserver/utils/boundingbox"
|
23
|
+
autoload :ShapefileInfo, "rgeoserver/utils/shapefile_info"
|
24
|
+
|
22
25
|
require 'restclient'
|
23
26
|
require 'nokogiri'
|
24
27
|
require 'time'
|
@@ -30,7 +33,7 @@ module RGeoServer
|
|
30
33
|
|
31
34
|
def self.catalog
|
32
35
|
@catalog ||= self.connect(self.default_config.geoserver)
|
33
|
-
end
|
36
|
+
end
|
34
37
|
|
35
38
|
def self.catalog= catalog
|
36
39
|
@catalog = catalog
|
data/lib/rgeoserver/catalog.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
module RGeoServer
|
2
2
|
# This class represents the main class of the data model.
|
3
|
-
# Refer to
|
3
|
+
# Refer to
|
4
4
|
# - http://geoserver.org/display/GEOS/Catalog+Design
|
5
5
|
# - http://docs.geoserver.org/stable/en/user/restconfig/rest-config-api.html#workspaces
|
6
6
|
|
7
7
|
class Catalog
|
8
8
|
include RGeoServer::RestApiClient
|
9
|
-
|
9
|
+
|
10
10
|
attr_reader :config
|
11
11
|
|
12
12
|
# @param [OrderedHash] options
|
@@ -27,14 +27,14 @@ module RGeoServer
|
|
27
27
|
end
|
28
28
|
|
29
29
|
#== Resources
|
30
|
-
|
30
|
+
|
31
31
|
# Shortcut to ResourceInfo.list to this catalog. See ResourceInfo#list
|
32
32
|
# @param [RGeoServer::ResourceInfo.class] klass
|
33
33
|
# @param [RGeoServer::Catalog] catalog
|
34
34
|
# @param [Array<String>] names
|
35
35
|
# @param [Hash] options
|
36
36
|
# @param [bool] check_remote if already exists in catalog and cache it
|
37
|
-
# @yield [RGeoServer::ResourceInfo]
|
37
|
+
# @yield [RGeoServer::ResourceInfo]
|
38
38
|
def list klass, names, options, check_remote = false, &block
|
39
39
|
ResourceInfo.list klass, self, names, options, check_remote, &block
|
40
40
|
end
|
@@ -44,12 +44,12 @@ module RGeoServer
|
|
44
44
|
# List of available workspaces
|
45
45
|
# @return [Array<RGeoServer::Workspace>]
|
46
46
|
def get_workspaces &block
|
47
|
-
response = self.search :workspaces => nil
|
47
|
+
response = self.search :workspaces => nil
|
48
48
|
doc = Nokogiri::XML(response)
|
49
49
|
workspaces = doc.xpath(Workspace.root_xpath).collect{|w| w.text.to_s }
|
50
50
|
list Workspace, workspaces, {}, &block
|
51
51
|
end
|
52
|
-
|
52
|
+
|
53
53
|
# @param [String] workspace name
|
54
54
|
# @return [RGeoServer::Workspace]
|
55
55
|
def get_workspace workspace
|
@@ -66,17 +66,17 @@ module RGeoServer
|
|
66
66
|
name = doc.at_xpath("#{Workspace.member_xpath}/name/text()").to_s
|
67
67
|
return Workspace.new self, :name => name
|
68
68
|
end
|
69
|
-
|
69
|
+
|
70
70
|
# Assign default workspace
|
71
71
|
# @param [String] workspace name
|
72
72
|
def set_default_workspace workspace
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
73
|
+
raise TypeError, "Workspace name must be a string" unless workspace.instance_of? String
|
74
|
+
dws = Workspace.new self, :name => 'default'
|
75
|
+
dws.name = workspace # This creates a new workspace if name is new
|
76
|
+
dws.save
|
77
|
+
dws
|
78
78
|
end
|
79
|
-
|
79
|
+
|
80
80
|
# @param [String] store
|
81
81
|
# @param [String] workspace
|
82
82
|
def reassign_workspace store, workspace
|
@@ -87,13 +87,16 @@ module RGeoServer
|
|
87
87
|
|
88
88
|
# List of available layers
|
89
89
|
# @return [Array<RGeoServer::Layer>]
|
90
|
-
def get_layers &block
|
91
|
-
response = self.search :layers => nil
|
90
|
+
def get_layers options = {}, &block
|
91
|
+
response = self.search :layers => nil
|
92
92
|
doc = Nokogiri::XML(response)
|
93
|
-
|
94
|
-
|
93
|
+
workspace_name = Workspace === options[:workspace] ? options[:workspace].name : options[:workspace]
|
94
|
+
layer_nodes = doc.xpath(Layer.root_xpath).collect{|l| l.text.to_s }
|
95
|
+
layers = list(Layer, layer_nodes, {}, &block)
|
96
|
+
layers = layers.find_all { |layer| layer.workspace.name == workspace_name } if options[:workspace]
|
97
|
+
layers
|
95
98
|
end
|
96
|
-
|
99
|
+
|
97
100
|
# @param [String] layer name
|
98
101
|
# @return [RGeoServer::Layer]
|
99
102
|
def get_layer layer
|
@@ -103,17 +106,41 @@ module RGeoServer
|
|
103
106
|
return Layer.new self, :name => name
|
104
107
|
end
|
105
108
|
|
109
|
+
#= LayerGroups
|
110
|
+
|
111
|
+
# List of available layer groups
|
112
|
+
# @return [Array<RGeoServer::LayerGroup>]
|
113
|
+
def get_layergroups options = {}, &block
|
114
|
+
response = unless options[:workspace]
|
115
|
+
self.search :layergroups => nil
|
116
|
+
else
|
117
|
+
self.search :workspaces => options[:workspace], :layergroups => nil
|
118
|
+
end
|
119
|
+
doc = Nokogiri::XML(response)
|
120
|
+
layer_groups = doc.xpath(LayerGroup.root_xpath).collect{|l| l.text.to_s }.map(&:strip)
|
121
|
+
list LayerGroup, layer_groups, {workspace: options[:workspace]}, &block
|
122
|
+
end
|
123
|
+
|
124
|
+
# @param [String] layer group name
|
125
|
+
# @return [RGeoServer::LayerGroup]
|
126
|
+
def get_layergroup layergroup
|
127
|
+
response = self.search :layergroups => layergroup
|
128
|
+
doc = Nokogiri::XML(response)
|
129
|
+
name = doc.at_xpath("#{LayerGroup.member_xpath}/name/text()").to_s
|
130
|
+
return LayerGroup.new self, :name => name
|
131
|
+
end
|
132
|
+
|
106
133
|
#= Styles (SLD Style Layer Descriptor)
|
107
134
|
|
108
135
|
# List of available styles
|
109
136
|
# @return [Array<RGeoServer::Style>]
|
110
137
|
def get_styles &block
|
111
|
-
response = self.search :styles => nil
|
138
|
+
response = self.search :styles => nil
|
112
139
|
doc = Nokogiri::XML(response)
|
113
140
|
styles = doc.xpath(Style.root_xpath).collect{|l| l.text.to_s }
|
114
141
|
list Style, styles, {}, &block
|
115
142
|
end
|
116
|
-
|
143
|
+
|
117
144
|
# @param [String] style name
|
118
145
|
# @return [RGeoServer::Style]
|
119
146
|
def get_style style
|
@@ -125,40 +152,40 @@ module RGeoServer
|
|
125
152
|
|
126
153
|
|
127
154
|
#= Namespaces
|
128
|
-
|
155
|
+
|
129
156
|
# List of available namespaces
|
130
157
|
# @return [Array<RGeoServer::Namespace>]
|
131
|
-
def get_namespaces
|
158
|
+
def get_namespaces
|
132
159
|
raise NotImplementedError
|
133
|
-
end
|
160
|
+
end
|
134
161
|
|
135
162
|
# @return [RGeoServer::Namespace]
|
136
|
-
def get_default_namespace
|
163
|
+
def get_default_namespace
|
137
164
|
response = self.search :namespaces => 'default'
|
138
165
|
doc = Nokogiri::XML(response)
|
139
166
|
name = doc.at_xpath("#{Namespace.member_xpath}/prefix/text()").to_s
|
140
167
|
uri = doc.at_xpath("#{Namespace.member_xpath}/uri/text()").to_s
|
141
|
-
return Namespace.new self, :name => name, :uri => uri
|
142
|
-
end
|
168
|
+
return Namespace.new self, :name => name, :uri => uri
|
169
|
+
end
|
143
170
|
|
144
|
-
def set_default_namespace id, prefix, uri
|
171
|
+
def set_default_namespace id, prefix, uri
|
145
172
|
raise NotImplementedError
|
146
|
-
end
|
173
|
+
end
|
147
174
|
|
148
175
|
#= Data Stores (Vector datasets)
|
149
176
|
|
150
177
|
# List of vector based spatial data
|
151
178
|
# @param [String] workspace
|
152
179
|
# @return [Array<RGeoServer::DataStore>]
|
153
|
-
def get_data_stores workspace = nil
|
154
|
-
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
180
|
+
def get_data_stores workspace = nil
|
181
|
+
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
155
182
|
ds = []
|
156
|
-
ws.each{ |w| ds += w.data_stores if w.data_stores }
|
157
|
-
ds
|
183
|
+
ws.each{ |w| ds += w.data_stores if w.data_stores }
|
184
|
+
ds
|
158
185
|
end
|
159
|
-
|
186
|
+
|
160
187
|
# @param [String] workspace
|
161
|
-
# @param [String] datastore
|
188
|
+
# @param [String] datastore
|
162
189
|
# @return [RGeoServer::DataStore]
|
163
190
|
def get_data_store workspace, datastore
|
164
191
|
response = self.search({:workspaces => workspace, :name => datastore})
|
@@ -166,38 +193,38 @@ module RGeoServer
|
|
166
193
|
name = doc.at_xpath(DataStore.member_xpath)
|
167
194
|
return DataStore.new self, workspace, name.text if name
|
168
195
|
end
|
169
|
-
|
196
|
+
|
170
197
|
# List of feature types
|
171
198
|
# @param [String] workspace
|
172
|
-
# @param [String] datastore
|
199
|
+
# @param [String] datastore
|
173
200
|
# @return [Array<RGeoServer::FeatureType>]
|
174
201
|
def get_feature_types workspace, datastore
|
175
|
-
raise NotImplementedError
|
202
|
+
raise NotImplementedError
|
176
203
|
end
|
177
204
|
|
178
205
|
# @param [String] workspace
|
179
|
-
# @param [String] datastore
|
180
|
-
# @param [String] featuretype_id
|
206
|
+
# @param [String] datastore
|
207
|
+
# @param [String] featuretype_id
|
181
208
|
# @return [RGeoServer::FeatureType]
|
182
209
|
def get_feature_type workspace, datastore, featuretype_id
|
183
|
-
raise NotImplementedError
|
184
|
-
end
|
210
|
+
raise NotImplementedError
|
211
|
+
end
|
185
212
|
|
186
213
|
|
187
|
-
#= Coverages (Raster datasets)
|
188
|
-
|
214
|
+
#= Coverages (Raster datasets)
|
215
|
+
|
189
216
|
# List of coverage stores
|
190
217
|
# @param [String] workspace
|
191
218
|
# @return [Array<RGeoServer::CoverageStore>]
|
192
219
|
def get_coverage_stores workspace = nil
|
193
|
-
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
220
|
+
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
194
221
|
cs = []
|
195
|
-
ws.each{ |w| cs += w.coverage_stores if w.coverage_stores }
|
196
|
-
cs
|
222
|
+
ws.each{ |w| cs += w.coverage_stores if w.coverage_stores }
|
223
|
+
cs
|
197
224
|
end
|
198
225
|
|
199
226
|
# @param [String] workspace
|
200
|
-
# @param [String] coveragestore
|
227
|
+
# @param [String] coveragestore
|
201
228
|
# @return [RGeoServer::CoverageStore]
|
202
229
|
def get_coverage_store workspace, coveragestore
|
203
230
|
cs = CoverageStore.new self, :workspace => workspace, :name => coveragestore
|
@@ -206,43 +233,43 @@ module RGeoServer
|
|
206
233
|
|
207
234
|
def get_coverage workspace, coverage_store, coverage
|
208
235
|
c = Coverage.new self, :workspace => workspace, :coverage_store => coverage_store, :name => coverage
|
209
|
-
return c.new?? nil : c
|
236
|
+
return c.new?? nil : c
|
210
237
|
end
|
211
238
|
|
212
239
|
#= WMS Stores (Web Map Services)
|
213
240
|
|
214
241
|
# List of WMS stores.
|
215
|
-
# @param [String] workspace
|
242
|
+
# @param [String] workspace
|
216
243
|
# @return [Array<RGeoServer::WmsStore>]
|
217
244
|
def get_wms_stores workspace = nil
|
218
|
-
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
245
|
+
ws = workspace.nil?? get_workspaces : [get_workspace(workspace)]
|
219
246
|
cs = []
|
220
|
-
ws.each{ |w| cs += w.wms_stores if w.wms_stores }
|
221
|
-
cs
|
247
|
+
ws.each{ |w| cs += w.wms_stores if w.wms_stores }
|
248
|
+
cs
|
222
249
|
end
|
223
250
|
|
224
251
|
# @param [String] workspace
|
225
|
-
# @param [String] wmsstore
|
252
|
+
# @param [String] wmsstore
|
226
253
|
# @return [RGeoServer::WmsStore]
|
227
254
|
def get_wms_store workspace, wmsstore
|
228
255
|
response = self.search({:workspaces => workspace, :name => wmsstore})
|
229
256
|
doc = Nokogiri::XML(response)
|
230
257
|
name = doc.at_xpath(WmsStore.member_xpath)
|
231
258
|
return WmsStore.new self, workspace, name.text if name
|
232
|
-
end
|
259
|
+
end
|
233
260
|
|
234
261
|
#= Configuration reloading
|
235
262
|
# Reloads the catalog and configuration from disk. This operation is used to reload GeoServer in cases where an external tool has modified the on disk configuration. This operation will also force GeoServer to drop any internal caches and reconnect to all data stores.
|
236
263
|
def reload
|
237
|
-
do_url 'reload', :put
|
264
|
+
do_url 'reload', :put
|
238
265
|
end
|
239
266
|
|
240
267
|
#= Resource reset
|
241
268
|
# Resets all store/raster/schema caches and starts fresh. This operation is used to force GeoServer to drop all caches and stores and reconnect fresh to each of them first time they are needed by a request. This is useful in case the stores themselves cache some information about the data structures they manage that changed in the meantime.
|
242
269
|
def reset
|
243
270
|
do_url 'reset', :put
|
244
|
-
end
|
245
|
-
|
271
|
+
end
|
272
|
+
|
246
273
|
end
|
247
274
|
|
248
275
|
end
|
data/lib/rgeoserver/datastore.rb
CHANGED
@@ -3,7 +3,27 @@ module RGeoServer
|
|
3
3
|
# A data store is a source of spatial data that is vector based. It can be a file in the case of a Shapefile, a database in the case of PostGIS, or a server in the case of a remote Web Feature Service.
|
4
4
|
class DataStore < ResourceInfo
|
5
5
|
|
6
|
-
|
6
|
+
class DataStoreAlreadyExists < StandardError
|
7
|
+
def initialize(name)
|
8
|
+
@name = name
|
9
|
+
end
|
10
|
+
|
11
|
+
def message
|
12
|
+
"The DataStore '#{@name}' already exists and can not be replaced."
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class DataTypeNotExpected < StandardError
|
17
|
+
def initialize(data_type)
|
18
|
+
@data_type = data_type
|
19
|
+
end
|
20
|
+
|
21
|
+
def message
|
22
|
+
"The DataStore does not not accept the data type '#{@data_type}'."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
OBJ_ATTRIBUTES = {:enabled => "enabled", :catalog => "catalog", :workspace => "workspace", :name => "name", :connection_parameters => "connection_parameters"}
|
7
27
|
OBJ_DEFAULT_ATTRIBUTES = {:enabled => 'true', :catalog => nil, :workspace => nil, :name => nil, :connection_parameters => {}}
|
8
28
|
define_attribute_methods OBJ_ATTRIBUTES.keys
|
9
29
|
update_attribute_accessors OBJ_ATTRIBUTES
|
@@ -42,13 +62,13 @@ module RGeoServer
|
|
42
62
|
builder = Nokogiri::XML::Builder.new do |xml|
|
43
63
|
xml.dataStore {
|
44
64
|
xml.name @name
|
45
|
-
xml.enabled
|
65
|
+
xml.enabled enabled
|
46
66
|
xml.connectionParameters { # this could be empty
|
47
|
-
|
67
|
+
connection_parameters.each_pair { |k,v|
|
48
68
|
xml.entry(:key => k) {
|
49
69
|
xml.text v
|
50
70
|
}
|
51
|
-
} unless
|
71
|
+
} unless connection_parameters.nil? || connection_parameters.empty?
|
52
72
|
}
|
53
73
|
}
|
54
74
|
end
|
@@ -58,7 +78,7 @@ module RGeoServer
|
|
58
78
|
# @param [RGeoServer::Catalog] catalog
|
59
79
|
# @param [RGeoServer::Workspace|String] workspace
|
60
80
|
# @param [String] name
|
61
|
-
def initialize catalog, options
|
81
|
+
def initialize catalog, options
|
62
82
|
super({})
|
63
83
|
_run_initialize_callbacks do
|
64
84
|
@catalog = catalog
|
@@ -70,32 +90,86 @@ module RGeoServer
|
|
70
90
|
else
|
71
91
|
raise "Not a valid workspace"
|
72
92
|
end
|
73
|
-
|
93
|
+
|
74
94
|
@name = options[:name].strip
|
75
95
|
@route = route
|
76
|
-
end
|
96
|
+
end
|
77
97
|
end
|
78
98
|
|
79
99
|
def featuretypes &block
|
80
100
|
self.class.list FeatureType, @catalog, profile['featureTypes'], {:workspace => @workspace, :data_store => self}, check_remote = true, &block
|
81
101
|
end
|
82
102
|
|
103
|
+
# @param [String] file_path
|
104
|
+
# @param [Hash] options { data_type: [:shapefile] }. optional
|
105
|
+
def upload_file file_path, options = {}
|
106
|
+
raise DataStoreAlreadyExists, @name unless new?
|
107
|
+
|
108
|
+
options = options.dup
|
109
|
+
|
110
|
+
data_type = options.delete(:data_type) || :shapefile
|
111
|
+
data_type = data_type.to_sym
|
112
|
+
|
113
|
+
publish = options.delete(:publish) || false
|
114
|
+
|
115
|
+
upload_url_suffix = case data_type
|
116
|
+
when :shapefile then "#{update_route}/file.shp"
|
117
|
+
else
|
118
|
+
raise DataTypeNotExpected, data_type
|
119
|
+
end
|
120
|
+
|
121
|
+
@catalog.client[upload_url_suffix].put File.read(file_path), :content_type => 'application/zip'
|
122
|
+
|
123
|
+
clear
|
124
|
+
connection_parameters['url'] = connection_parameters['url'].gsub(/.*data/, '').insert(0, 'file:data') #correct to relative path
|
125
|
+
save
|
126
|
+
clear
|
127
|
+
|
128
|
+
if publish
|
129
|
+
ft = RGeoServer::FeatureType.new @catalog, :workspace => @workspace, :data_store => self, :name => @name
|
130
|
+
ft.title = ft.name.capitalize
|
131
|
+
ft.abstract = ft.name.capitalize
|
132
|
+
ft.enabled = true
|
133
|
+
|
134
|
+
bounds = case data_type
|
135
|
+
when :shapefile
|
136
|
+
shpInfo = ShapefileInfo.new file_path
|
137
|
+
shpInfo.bounds
|
138
|
+
else
|
139
|
+
raise DataTypeNotExpected, data_type
|
140
|
+
end
|
141
|
+
|
142
|
+
ft.native_bounds['minx'], ft.native_bounds['miny'], ft.native_bounds['maxx'], ft.native_bounds['maxy'] =
|
143
|
+
bounds.to_a
|
144
|
+
ft.projection_policy = :force
|
145
|
+
ft.save
|
146
|
+
|
147
|
+
layers = catalog.get_layers workspace: @workspace
|
148
|
+
layers.find_all{ |layer| layer.name == ft.name }.each do |layer|
|
149
|
+
layer.enabled = true
|
150
|
+
layer.save
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
self
|
155
|
+
end
|
156
|
+
|
83
157
|
def profile_xml_to_hash profile_xml
|
84
158
|
doc = profile_xml_to_ng profile_xml
|
85
159
|
h = {
|
86
|
-
"name" => doc.at_xpath('//name').text.strip,
|
160
|
+
"name" => doc.at_xpath('//name').text.strip,
|
87
161
|
"enabled" => doc.at_xpath('//enabled/text()').to_s,
|
88
|
-
"
|
162
|
+
"connection_parameters" => doc.xpath('//connectionParameters/entry').inject({}){ |h, e| h.merge(e['key']=> e.text.to_s) }
|
89
163
|
}
|
90
|
-
doc.xpath('//featureTypes/atom:link/@href', "xmlns:atom"=>"http://www.w3.org/2005/Atom" ).each{ |l|
|
164
|
+
doc.xpath('//featureTypes/atom:link/@href', "xmlns:atom"=>"http://www.w3.org/2005/Atom" ).each{ |l|
|
91
165
|
h["featureTypes"] = begin
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
h
|
166
|
+
response = @catalog.do_url l.text
|
167
|
+
Nokogiri::XML(response).xpath('//name/text()').collect{ |a| a.text.strip }
|
168
|
+
rescue RestClient::ResourceNotFound
|
169
|
+
[]
|
170
|
+
end.freeze
|
171
|
+
}
|
172
|
+
h
|
99
173
|
end
|
100
174
|
end
|
101
|
-
end
|
175
|
+
end
|