rgeoserver 0.5.6 → 0.5.7
Sign up to get free protection for your applications and to get access to all the features.
- data/README.rdoc +27 -8
- data/VERSION +1 -1
- data/config/config_defaults.yml +1 -0
- data/examples/batch_example.rb +105 -0
- data/examples/cluster_demo.rb +30 -0
- data/lib/rgeoserver.rb +1 -0
- data/lib/rgeoserver/catalog.rb +24 -19
- data/lib/rgeoserver/coverage.rb +43 -6
- data/lib/rgeoserver/coveragestore.rb +5 -1
- data/lib/rgeoserver/datastore.rb +3 -3
- data/lib/rgeoserver/featuretype.rb +54 -4
- data/lib/rgeoserver/layer.rb +115 -5
- data/lib/rgeoserver/layergroup.rb +1 -1
- data/lib/rgeoserver/namespace.rb +63 -0
- data/lib/rgeoserver/resource.rb +23 -3
- data/lib/rgeoserver/rest_api_client.rb +33 -18
- data/rgeoserver.gemspec +1 -1
- data/spec/fixtures/datasets/raster/test.tif +0 -0
- data/spec/functional/resource_spec.rb +13 -0
- data/spec/{lib/integration_test_spec.rb → integration/geoserver_spec.rb} +149 -9
- metadata +37 -30
@@ -2,8 +2,8 @@
|
|
2
2
|
module RGeoServer
|
3
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
4
|
class FeatureType < ResourceInfo
|
5
|
-
OBJ_ATTRIBUTES = {:catalog => "catalog", :name => "name", :workspace => "workspace", :enabled => "enabled" }
|
6
|
-
OBJ_DEFAULT_ATTRIBUTES = {:catalog => nil, :workspace => nil, :coverage_store => nil, :name => nil, :enabled => "false" }
|
5
|
+
OBJ_ATTRIBUTES = {:catalog => "catalog", :name => "name", :workspace => "workspace", :enabled => "enabled", :metadata_links => "metadataLinks", :title => "title", :abstract => "abstract" }
|
6
|
+
OBJ_DEFAULT_ATTRIBUTES = {:catalog => nil, :workspace => nil, :coverage_store => nil, :name => nil, :enabled => "false", :metadata_links => [], :title => nil, :abtract => nil }
|
7
7
|
|
8
8
|
define_attribute_methods OBJ_ATTRIBUTES.keys
|
9
9
|
update_attribute_accessors OBJ_ATTRIBUTES
|
@@ -35,7 +35,24 @@ module RGeoServer
|
|
35
35
|
def message
|
36
36
|
builder = Nokogiri::XML::Builder.new do |xml|
|
37
37
|
xml.featureType {
|
38
|
-
xml.name @name
|
38
|
+
xml.name @name if new?
|
39
|
+
xml.enabled @enabled if (enabled_changed? || new?)
|
40
|
+
unless new?
|
41
|
+
xml.title @title
|
42
|
+
xml.abstract @abtract if abstract_changed?
|
43
|
+
xml.store(:class => 'featureType') {
|
44
|
+
xml.name @data_store.name
|
45
|
+
}
|
46
|
+
xml.metadataLinks {
|
47
|
+
@metadata_links.each{ |m|
|
48
|
+
xml.metadataLink {
|
49
|
+
xml.type_ m['type']
|
50
|
+
xml.metadataType m['metadataType']
|
51
|
+
xml.content m['content']
|
52
|
+
}
|
53
|
+
}
|
54
|
+
} if metadata_links_changed?
|
55
|
+
end
|
39
56
|
}
|
40
57
|
end
|
41
58
|
@message = builder.doc.to_xml
|
@@ -75,8 +92,41 @@ module RGeoServer
|
|
75
92
|
doc = profile_xml_to_ng profile_xml
|
76
93
|
h = {
|
77
94
|
"name" => doc.at_xpath('//name').text.strip,
|
95
|
+
"title" => doc.at_xpath('//title/text()').to_s,
|
96
|
+
"abstract" => doc.at_xpath('//abstract/text()').to_s,
|
78
97
|
"workspace" => @workspace.name,
|
79
|
-
"nativeName" => doc.at_xpath('//nativeName').to_s
|
98
|
+
"nativeName" => doc.at_xpath('//nativeName/text()').to_s,
|
99
|
+
"srs" => doc.at_xpath('//srs/text()').to_s,
|
100
|
+
"nativeBoundingBox" => {
|
101
|
+
'minx' => doc.at_xpath('//nativeBoundingBox/minx/text()').to_s,
|
102
|
+
'miny' => doc.at_xpath('//nativeBoundingBox/miny/text()').to_s,
|
103
|
+
'maxx' => doc.at_xpath('//nativeBoundingBox/maxx/text()').to_s,
|
104
|
+
'maxy' => doc.at_xpath('//nativeBoundingBox/maxy/text()').to_s,
|
105
|
+
'crs' => doc.at_xpath('//nativeBoundingBox/crs/text()').to_s
|
106
|
+
},
|
107
|
+
"latLonBoundingBox" => {
|
108
|
+
'minx' => doc.at_xpath('//latLonBoundingBox/minx/text()').to_s,
|
109
|
+
'miny' => doc.at_xpath('//latLonBoundingBox/miny/text()').to_s,
|
110
|
+
'maxx' => doc.at_xpath('//latLonBoundingBox/maxx/text()').to_s,
|
111
|
+
'maxy' => doc.at_xpath('//latLonBoundingBox/maxy/text()').to_s,
|
112
|
+
'crs' => doc.at_xpath('//latLonBoundingBox/crs/text()').to_s
|
113
|
+
},
|
114
|
+
"metadataLinks" => doc.xpath('//metadataLinks/metadataLink').collect{ |m|
|
115
|
+
{
|
116
|
+
'type' => m.at_xpath('//type/text()').to_s,
|
117
|
+
'metadataType' => m.at_xpath('//metadataType/text()').to_s,
|
118
|
+
'content' => m.at_xpath('//content/text()').to_s
|
119
|
+
}
|
120
|
+
},
|
121
|
+
"attributes" => doc.xpath('//attributes/attribute').collect{ |a|
|
122
|
+
{
|
123
|
+
'name' => a.at_xpath('//name/text()').to_s,
|
124
|
+
'minOccurs' => a.at_xpath('//minOccurs/text()').to_s,
|
125
|
+
'maxOccurs' => a.at_xpath('//maxOccurs/text()').to_s,
|
126
|
+
'nillable' => a.at_xpath('//nillable/text()').to_s,
|
127
|
+
'binding' => a.at_xpath('//binding/text()').to_s
|
128
|
+
}
|
129
|
+
}
|
80
130
|
}.freeze
|
81
131
|
h
|
82
132
|
end
|
data/lib/rgeoserver/layer.rb
CHANGED
@@ -3,8 +3,29 @@ module RGeoServer
|
|
3
3
|
# A layer is a published resource (feature type or coverage).
|
4
4
|
class Layer < ResourceInfo
|
5
5
|
|
6
|
-
OBJ_ATTRIBUTES = {:enabled => 'enabled', :catalog => 'catalog', :name => 'name', :default_style => 'default_style', :alternate_styles => 'alternate_styles', :metadata => 'metadata', :attribution => 'attribution', :layer_type => 'type' }
|
7
|
-
OBJ_DEFAULT_ATTRIBUTES = {
|
6
|
+
OBJ_ATTRIBUTES = {:enabled => 'enabled', :queryable => 'queryable', :path => 'path', :catalog => 'catalog', :name => 'name', :default_style => 'default_style', :alternate_styles => 'alternate_styles', :metadata => 'metadata', :attribution => 'attribution', :layer_type => 'type' }
|
7
|
+
OBJ_DEFAULT_ATTRIBUTES = {
|
8
|
+
:enabled => 'true',
|
9
|
+
:queryable => 'true',
|
10
|
+
:path => '/',
|
11
|
+
:catalog => nil,
|
12
|
+
:name => nil,
|
13
|
+
:default_style => nil,
|
14
|
+
:alternate_styles => [],
|
15
|
+
:metadata => {
|
16
|
+
'GWC.autoCacheStyles' => 'true',
|
17
|
+
'GWC.gutter' => '0',
|
18
|
+
'GWC.enabled' => 'true',
|
19
|
+
'GWC.cacheFormats' => 'image/jpeg,image/png',
|
20
|
+
'GWC.gridSets' => 'EPSG:4326,EPSG:900913'
|
21
|
+
},
|
22
|
+
:attribution => {
|
23
|
+
'logo_height' => '0',
|
24
|
+
'logo_width' => '0',
|
25
|
+
'title' => ''
|
26
|
+
},
|
27
|
+
:layer_type => nil
|
28
|
+
}
|
8
29
|
|
9
30
|
define_attribute_methods OBJ_ATTRIBUTES.keys
|
10
31
|
update_attribute_accessors OBJ_ATTRIBUTES
|
@@ -33,12 +54,18 @@ module RGeoServer
|
|
33
54
|
nil
|
34
55
|
end
|
35
56
|
|
57
|
+
def update_params name_route = @name
|
58
|
+
{ :layer => name_route }
|
59
|
+
end
|
60
|
+
|
36
61
|
def message
|
37
62
|
builder = Nokogiri::XML::Builder.new do |xml|
|
38
63
|
xml.layer {
|
39
|
-
|
40
|
-
xml.
|
41
|
-
xml.
|
64
|
+
xml.name @name
|
65
|
+
xml.path path
|
66
|
+
xml.type_ layer_type
|
67
|
+
xml.enabled @enabled
|
68
|
+
xml.queryable @queryable
|
42
69
|
xml.defaultStyle {
|
43
70
|
xml.name default_style
|
44
71
|
}
|
@@ -52,6 +79,13 @@ module RGeoServer
|
|
52
79
|
xml.resource(:class => resource.class.resource_name){
|
53
80
|
xml.name resource.name
|
54
81
|
} unless resource.nil?
|
82
|
+
xml.metadata {
|
83
|
+
metadata.each_pair { |k,v|
|
84
|
+
xml.entry(:key => k) {
|
85
|
+
xml.text v
|
86
|
+
}
|
87
|
+
}
|
88
|
+
}
|
55
89
|
xml.attribution {
|
56
90
|
xml.title attribution['title'] unless attribution['title'].empty?
|
57
91
|
xml.logoWidth attribution['logo_width']
|
@@ -130,8 +164,10 @@ module RGeoServer
|
|
130
164
|
"path" => doc.at_xpath('//path/text()').to_s,
|
131
165
|
"default_style" => doc.at_xpath('//defaultStyle/name/text()').to_s,
|
132
166
|
"alternate_styles" => doc.xpath('//styles/style/name/text()').collect{ |s| s.to_s},
|
167
|
+
# Types can be: VECTOR, RASTER, REMOTE, WMS
|
133
168
|
"type" => doc.at_xpath('//type/text()').to_s,
|
134
169
|
"enabled" => doc.at_xpath('//enabled/text()').to_s,
|
170
|
+
"queryable" => doc.at_xpath('//queryable/text()').to_s,
|
135
171
|
"attribution" => {
|
136
172
|
"title" => doc.at_xpath('//attribution/title/text()').to_s,
|
137
173
|
"logo_width" => doc.at_xpath('//attribution/logoWidth/text()').to_s,
|
@@ -148,5 +184,79 @@ module RGeoServer
|
|
148
184
|
h
|
149
185
|
end
|
150
186
|
|
187
|
+
def workspace
|
188
|
+
resource.workspace
|
189
|
+
end
|
190
|
+
|
191
|
+
#= GeoWebCache Operations for this layer
|
192
|
+
# See http://geowebcache.org/docs/current/rest/seed.html
|
193
|
+
# See RGeoServer::Catalog.seed_terminate for stopping pending and/or running tasks for any layer
|
194
|
+
#
|
195
|
+
# Example:
|
196
|
+
# > lyr = RGeoServer::Layer.new catalog, :name => 'Arc_Sample'
|
197
|
+
# > options = {
|
198
|
+
# :srs => {:number => 4326 },
|
199
|
+
# :zoomStart => 1,
|
200
|
+
# :zoomStop => 12,
|
201
|
+
# :format => 'image/png',
|
202
|
+
# :threadCount => 1
|
203
|
+
# }
|
204
|
+
# > lyr.seed :issue, options
|
205
|
+
|
206
|
+
# @param[String] operation
|
207
|
+
# @option operation[Symbol] :issue seed
|
208
|
+
# @option operation[Symbol] :truncate seed
|
209
|
+
# @option operation[Symbol] :status of the seeding thread
|
210
|
+
# @param[Hash] options for seed message. Read the documentation
|
211
|
+
def seed operation, options
|
212
|
+
op = operation.to_sym
|
213
|
+
sub_path = "seed/#{prefixed_name}.xml"
|
214
|
+
case op
|
215
|
+
when :issue
|
216
|
+
@catalog.do_url sub_path, :post, build_seed_request(:seed, options), {}, @catalog.gwc_client
|
217
|
+
when :truncate
|
218
|
+
@catalog.do_url sub_path, :post, build_seed_request(:truncate, options), {}, @catalog.gwc_client
|
219
|
+
when :status
|
220
|
+
raise NotImplementedError
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
# @param[Hash] options for seed message
|
225
|
+
def build_seed_request operation, options
|
226
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
227
|
+
xml.seedRequest {
|
228
|
+
xml.name prefixed_name
|
229
|
+
|
230
|
+
xml.srs {
|
231
|
+
xml.number options[:srs][:number]
|
232
|
+
} unless options[:srs].nil? #&& options[:srs].is_a?(Hash)
|
233
|
+
|
234
|
+
xml.bounds {
|
235
|
+
xml.coords {
|
236
|
+
options[:bounds][:coords].each { |dbl|
|
237
|
+
xml.double dbl
|
238
|
+
}
|
239
|
+
}
|
240
|
+
} unless options[:bounds].nil?
|
241
|
+
|
242
|
+
xml.type_ operation
|
243
|
+
|
244
|
+
[:gridSetId, :zoomStart, :zoomStop, :format, :threadCount].each { |p|
|
245
|
+
eval "xml.#{p.to_s} options[p]" unless options[p].nil?
|
246
|
+
}
|
247
|
+
|
248
|
+
xml.parameters {
|
249
|
+
options[:parameters].each_pair { |k,v|
|
250
|
+
xml.entry {
|
251
|
+
xml.string k.upcase
|
252
|
+
xml.string v
|
253
|
+
}
|
254
|
+
}
|
255
|
+
} if options[:parameters].is_a?(Hash)
|
256
|
+
}
|
257
|
+
end
|
258
|
+
return builder.doc.to_xml
|
259
|
+
end
|
151
260
|
end
|
261
|
+
|
152
262
|
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
|
2
|
+
module RGeoServer
|
3
|
+
# A namespace is a uniquely identifiable grouping of feature types. A namespaces is identified by a prefix and a uri.
|
4
|
+
class Namespace < ResourceInfo
|
5
|
+
|
6
|
+
OBJ_ATTRIBUTES = {:enabled => 'enabled', :catalog => 'catalog', :name => 'prefix', :uri => 'uri' }
|
7
|
+
OBJ_DEFAULT_ATTRIBUTES = {:enabled => 'true', :catalog => nil, :name => nil }
|
8
|
+
|
9
|
+
define_attribute_methods OBJ_ATTRIBUTES.keys
|
10
|
+
update_attribute_accessors OBJ_ATTRIBUTES
|
11
|
+
|
12
|
+
@@route = "namespaces"
|
13
|
+
@@resource_name = "namespace"
|
14
|
+
|
15
|
+
def self.resource_name
|
16
|
+
@@resource_name
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.root_xpath
|
20
|
+
"//#{@@route}/#{@@resource_name}"
|
21
|
+
end
|
22
|
+
|
23
|
+
def self.member_xpath
|
24
|
+
"//#{resource_name}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def route
|
28
|
+
@@route
|
29
|
+
end
|
30
|
+
|
31
|
+
def message
|
32
|
+
builder = Nokogiri::XML::Builder.new do |xml|
|
33
|
+
xml.namespace {
|
34
|
+
xml.prefix @name
|
35
|
+
xml.uri @uri
|
36
|
+
}
|
37
|
+
end
|
38
|
+
return builder.doc.to_xml
|
39
|
+
end
|
40
|
+
|
41
|
+
# @param [RGeoServer::Catalog] catalog
|
42
|
+
# @param [Hash] options
|
43
|
+
def initialize catalog, options
|
44
|
+
super({})
|
45
|
+
_run_initialize_callbacks do
|
46
|
+
@catalog = catalog
|
47
|
+
@name = options[:name].strip
|
48
|
+
@uri = options[:uri] if options[:uri]
|
49
|
+
end
|
50
|
+
@route = route
|
51
|
+
end
|
52
|
+
|
53
|
+
def profile_xml_to_hash profile_xml
|
54
|
+
doc = profile_xml_to_ng profile_xml
|
55
|
+
h = {
|
56
|
+
'name' => doc.at_xpath('//namespace/prefix/text()').to_s,
|
57
|
+
'uri' => doc.at_xpath('//namespace/uri/text()').to_s
|
58
|
+
}.freeze
|
59
|
+
h
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
data/lib/rgeoserver/resource.rb
CHANGED
@@ -55,6 +55,12 @@ module RGeoServer
|
|
55
55
|
def to_s
|
56
56
|
"#{self.class.name}: #{name}"
|
57
57
|
end
|
58
|
+
|
59
|
+
# Return full name of resource with namespace prefix
|
60
|
+
def prefixed_name
|
61
|
+
return "#{workspace.name}:#{name}" if self.respond_to?(:workspace)
|
62
|
+
raise "Workspace is not defined for this resource"
|
63
|
+
end
|
58
64
|
|
59
65
|
def create_method
|
60
66
|
:post
|
@@ -63,6 +69,12 @@ module RGeoServer
|
|
63
69
|
def update_method
|
64
70
|
:put
|
65
71
|
end
|
72
|
+
|
73
|
+
# We pass the old name "name_route" in case the name of the resource is being edited
|
74
|
+
# Child classes should implement this
|
75
|
+
def update_params name_route = name
|
76
|
+
{ self.class.resource_name.downcase.to_sym => name_route }
|
77
|
+
end
|
66
78
|
|
67
79
|
# Modify or save the resource
|
68
80
|
# @param [Hash] options / query parameters
|
@@ -71,7 +83,15 @@ module RGeoServer
|
|
71
83
|
@previously_changed = changes
|
72
84
|
@changed_attributes.clear
|
73
85
|
run_callbacks :save do
|
74
|
-
|
86
|
+
unless @previously_changed[:name].nil?
|
87
|
+
old_name, new_name = @previously_changed[:name]
|
88
|
+
name_route = old_name if old_name != new_name
|
89
|
+
update = true
|
90
|
+
else
|
91
|
+
name_route = name
|
92
|
+
update = false
|
93
|
+
end
|
94
|
+
if !update && new?
|
75
95
|
if self.respond_to?(:create_route)
|
76
96
|
raise "Resource cannot be created directly" if create_route.nil?
|
77
97
|
route = create_route
|
@@ -83,8 +103,8 @@ module RGeoServer
|
|
83
103
|
@catalog.add(route, message, create_method, options)
|
84
104
|
clear
|
85
105
|
else
|
86
|
-
options =
|
87
|
-
route =
|
106
|
+
options = update_params(name_route).merge(options)
|
107
|
+
route = {@route => name_route}
|
88
108
|
@catalog.modify(route, message, update_method, options) #unless changes.empty?
|
89
109
|
end
|
90
110
|
|
@@ -10,11 +10,24 @@ module RGeoServer
|
|
10
10
|
include RGeoServer::GeoServerUrlHelpers
|
11
11
|
include ActiveSupport::Benchmarkable
|
12
12
|
|
13
|
+
# Instantiates a rest client with passed configuration
|
14
|
+
# @param [Hash] c configuration
|
15
|
+
# return <RestClient::Resource>
|
16
|
+
def rest_client c
|
17
|
+
RestClient::Resource.new(c[:url], :user => c[:user], :password => c[:password], :headers => c[:headers])
|
18
|
+
end
|
19
|
+
|
13
20
|
def client config = {}
|
21
|
+
@client ||= rest_client(self.config.merge(config))
|
22
|
+
end
|
23
|
+
|
24
|
+
def gwc_client config = {}
|
14
25
|
c = self.config.merge(config)
|
15
|
-
|
26
|
+
c[:url] = c[:geowebcache_url]
|
27
|
+
@gwc_client ||= rest_client(c)
|
16
28
|
end
|
17
29
|
|
30
|
+
|
18
31
|
def headers format
|
19
32
|
sym = :xml || format.to_sym
|
20
33
|
{:accept => sym, :content_type=> sym}
|
@@ -36,23 +49,25 @@ module RGeoServer
|
|
36
49
|
end
|
37
50
|
end
|
38
51
|
|
39
|
-
# Do an action on an arbitrary URL within the catalog
|
40
|
-
# Default method is GET
|
41
|
-
# @param [String]
|
42
|
-
# @param [String] method
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
52
|
+
# Do an action on an arbitrary URL path within the catalog
|
53
|
+
# Default method is GET
|
54
|
+
# @param [String] sub_url
|
55
|
+
# @param [String] method
|
56
|
+
# @param [String] data payload
|
57
|
+
# @param [Hash] options for request
|
58
|
+
def do_url sub_url, method = :get, data = nil, options = {}, client = client
|
59
|
+
sub_url.slice! client.url
|
60
|
+
fetcher = client[sub_url]
|
61
|
+
fetcher.options.merge(options)
|
62
|
+
begin
|
63
|
+
return fetcher.get if method == :get
|
64
|
+
fetcher.send method, data
|
65
|
+
rescue RestClient::InternalServerError => e
|
66
|
+
$logger.error e.response
|
67
|
+
$logger.flush if $logger.respond_to? :flush
|
68
|
+
raise GeoServerInvalidRequest, "Error fetching URL: #{sub_url}. See $logger for details"
|
69
|
+
end
|
70
|
+
end
|
56
71
|
|
57
72
|
# Add resource to the catalog
|
58
73
|
# @param [String] what
|
data/rgeoserver.gemspec
CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |s|
|
|
10
10
|
s.homepage = "http://github.com/rnz0/rgeoserver"
|
11
11
|
|
12
12
|
s.files = `git ls-files`.split("\n")
|
13
|
-
s.test_files = `git ls-files -- {
|
13
|
+
s.test_files = `git ls-files -- {examples,spec,features}/*`.split("\n")
|
14
14
|
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
15
15
|
s.require_paths = ["lib"]
|
16
16
|
|
Binary file
|