Mercurial > gemma
comparison pkg/wfs/capabilities.go @ 1689:6caf5cd6249e
WFS: Made golint happy.
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Sun, 30 Dec 2018 14:39:46 +0100 |
parents | 2dc7768be0e4 |
children | 02505fcff63c |
comparison
equal
deleted
inserted
replaced
1688:774174d09d30 | 1689:6caf5cd6249e |
---|---|
21 "strconv" | 21 "strconv" |
22 | 22 |
23 "golang.org/x/net/html/charset" | 23 "golang.org/x/net/html/charset" |
24 ) | 24 ) |
25 | 25 |
26 // Keyword stores a value. | |
26 type Keyword struct { | 27 type Keyword struct { |
27 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keyword"` | 28 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keyword"` |
28 Value string `xml:",cdata"` | 29 Value string `xml:",cdata"` |
29 } | 30 } |
31 | |
32 // Keywords stores a list of keywords. | |
30 type Keywords struct { | 33 type Keywords struct { |
31 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keywords"` | 34 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keywords"` |
32 Keywords []Keyword `xml:"Keyword"` | 35 Keywords []Keyword `xml:"Keyword"` |
33 } | 36 } |
34 | 37 |
38 // ServiceIdentification contains meta informations about a WFS. | |
35 type ServiceIdentification struct { | 39 type ServiceIdentification struct { |
36 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 ServiceIdentification"` | 40 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 ServiceIdentification"` |
37 Title string | 41 Title string |
38 Abstract string | 42 Abstract string |
39 Keywords Keywords `xml:"Keywords"` | 43 Keywords Keywords `xml:"Keywords"` |
40 ServiceType string | 44 ServiceType string |
41 ServiceTypeVersion string | 45 ServiceTypeVersion string |
42 } | 46 } |
43 | 47 |
48 // Get stores the link to the GET method | |
44 type Get struct { | 49 type Get struct { |
45 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Get"` | 50 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Get"` |
46 HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` | 51 HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` |
47 } | 52 } |
48 | 53 |
54 // Post stores the link to the POST method. | |
49 type Post struct { | 55 type Post struct { |
50 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Post"` | 56 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Post"` |
51 HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` | 57 HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` |
52 } | 58 } |
53 | 59 |
60 // HTTP is a container for HTTP methods. | |
54 type HTTP struct { | 61 type HTTP struct { |
55 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 HTTP"` | 62 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 HTTP"` |
56 Get *Get `xml:"Get"` | 63 Get *Get `xml:"Get"` |
57 Post *Post `xml:"Post"` | 64 Post *Post `xml:"Post"` |
58 } | 65 } |
59 | 66 |
67 // DCP wraps the HTTP container. | |
60 type DCP struct { | 68 type DCP struct { |
61 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DCP"` | 69 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DCP"` |
62 HTTP HTTP `xml:"HTTP"` | 70 HTTP HTTP `xml:"HTTP"` |
63 } | 71 } |
64 | 72 |
73 // Value is a simple string value. | |
65 type Value struct { | 74 type Value struct { |
66 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Value"` | 75 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Value"` |
67 Value string `xml:",cdata"` | 76 Value string `xml:",cdata"` |
68 } | 77 } |
69 | 78 |
79 // AllowedValues is list positive list of values. | |
70 type AllowedValues struct { | 80 type AllowedValues struct { |
71 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 AllowedValues"` | 81 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 AllowedValues"` |
72 Values []Value `xml:"Value"` | 82 Values []Value `xml:"Value"` |
73 } | 83 } |
74 | 84 |
85 // Parameter is a named parameter with a list of allowed values. | |
75 type Parameter struct { | 86 type Parameter struct { |
76 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Parameter"` | 87 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Parameter"` |
77 Name string `xml:"name,attr"` | 88 Name string `xml:"name,attr"` |
78 AllowedValues AllowedValues `xml:"AllowedValues"` | 89 AllowedValues AllowedValues `xml:"AllowedValues"` |
79 } | 90 } |
80 | 91 |
92 // DefaultValue is the default value of a constraint. | |
81 type DefaultValue struct { | 93 type DefaultValue struct { |
82 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DefaultValue"` | 94 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DefaultValue"` |
83 Value string `xml:",cdata"` | 95 Value string `xml:",cdata"` |
84 } | 96 } |
85 | 97 |
98 // Constraint is a named constraint with a list of allowed values | |
99 // and a default value. | |
86 type Constraint struct { | 100 type Constraint struct { |
87 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Constraint"` | 101 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Constraint"` |
88 Name string `xml:"name,attr"` | 102 Name string `xml:"name,attr"` |
89 AllowedValues AllowedValues `xml:"AllowedValues"` | 103 AllowedValues AllowedValues `xml:"AllowedValues"` |
90 DefaultValue *DefaultValue `xml:"DefaultValue"` | 104 DefaultValue *DefaultValue `xml:"DefaultValue"` |
91 } | 105 } |
92 | 106 |
107 // Operation contains informations of a WFS operation. | |
93 type Operation struct { | 108 type Operation struct { |
94 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Operation"` | 109 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Operation"` |
95 Name string `xml:"name,attr"` | 110 Name string `xml:"name,attr"` |
96 DCP DCP `xml:"DCP"` | 111 DCP DCP `xml:"DCP"` |
97 Parameters []*Parameter `xml:"Parameter"` | 112 Parameters []*Parameter `xml:"Parameter"` |
98 Constraints []*Constraint `xml:"Constraint"` | 113 Constraints []*Constraint `xml:"Constraint"` |
99 } | 114 } |
100 | 115 |
116 // OperationsMetadata is list of operations and constraints. | |
101 type OperationsMetadata struct { | 117 type OperationsMetadata struct { |
102 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 OperationsMetadata"` | 118 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 OperationsMetadata"` |
103 Operations []*Operation `xml:"Operation"` | 119 Operations []*Operation `xml:"Operation"` |
104 Constraints []*Constraint `xml:"Constraint"` | 120 Constraints []*Constraint `xml:"Constraint"` |
105 } | 121 } |
106 | 122 |
123 // WGS84BoundingBox is a bounding box feature type in WGS84. | |
107 type WGS84BoundingBox struct { | 124 type WGS84BoundingBox struct { |
108 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 WGS84BoundingBox"` | 125 XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 WGS84BoundingBox"` |
109 LowerCorner string `xml:"LowerCorner"` | 126 LowerCorner string `xml:"LowerCorner"` |
110 UpperCorner string `xml:"UpperCorner"` | 127 UpperCorner string `xml:"UpperCorner"` |
111 } | 128 } |
112 | 129 |
130 // FeatureType is layer served by the WFS: | |
113 type FeatureType struct { | 131 type FeatureType struct { |
114 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureType"` | 132 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureType"` |
115 Name string `xml:"Name"` | 133 Name string `xml:"Name"` |
116 Title string `xml:"Title"` | 134 Title string `xml:"Title"` |
117 Abstract string `xml:"Abstract"` | 135 Abstract string `xml:"Abstract"` |
133 OtherCRSs []string `xml:"OtherCRS"` | 151 OtherCRSs []string `xml:"OtherCRS"` |
134 WGS84BoundingBox *WGS84BoundingBox `xml:"WGS84BoundingBox"` | 152 WGS84BoundingBox *WGS84BoundingBox `xml:"WGS84BoundingBox"` |
135 Namespaces []xml.Name `xml:"-"` | 153 Namespaces []xml.Name `xml:"-"` |
136 } | 154 } |
137 | 155 |
156 // UnmarshalXML implements xml.Unmarshaler for better namespace handling. | |
138 func (ft *FeatureType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { | 157 func (ft *FeatureType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { |
139 // Filter out the namespaces for this feature type. | 158 // Filter out the namespaces for this feature type. |
140 var ns []xml.Name | 159 var ns []xml.Name |
141 for _, attr := range start.Attr { | 160 for _, attr := range start.Attr { |
142 if attr.Name.Space == "xmlns" { | 161 if attr.Name.Space == "xmlns" { |
150 *ft = FeatureType(sft) | 169 *ft = FeatureType(sft) |
151 ft.Namespaces = ns | 170 ft.Namespaces = ns |
152 return nil | 171 return nil |
153 } | 172 } |
154 | 173 |
174 // FeatureTypeList is the list of layers served by the WFS. | |
155 type FeatureTypeList struct { | 175 type FeatureTypeList struct { |
156 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureTypeList"` | 176 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureTypeList"` |
157 FeatureTypes []*FeatureType `xml:"FeatureType"` | 177 FeatureTypes []*FeatureType `xml:"FeatureType"` |
158 } | 178 } |
159 | 179 |
180 // Capabilities is the top level metadata struct. | |
160 type Capabilities struct { | 181 type Capabilities struct { |
161 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 WFS_Capabilities"` | 182 XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 WFS_Capabilities"` |
162 | 183 |
163 BaseURL string `xml:"-"` | 184 BaseURL string `xml:"-"` |
164 | 185 |
165 ServiceIdentification ServiceIdentification | 186 ServiceIdentification ServiceIdentification |
166 OperationsMetadata OperationsMetadata | 187 OperationsMetadata OperationsMetadata |
167 FeatureTypeList FeatureTypeList | 188 FeatureTypeList FeatureTypeList |
168 } | 189 } |
169 | 190 |
191 // FindOperation searches the capabilities for a specifc operation. | |
192 // Returns nil if not found. | |
170 func (c *Capabilities) FindOperation(name string) *Operation { | 193 func (c *Capabilities) FindOperation(name string) *Operation { |
171 for _, op := range c.OperationsMetadata.Operations { | 194 for _, op := range c.OperationsMetadata.Operations { |
172 if op.Name == name { | 195 if op.Name == name { |
173 return op | 196 return op |
174 } | 197 } |
175 } | 198 } |
176 return nil | 199 return nil |
177 } | 200 } |
178 | 201 |
179 func (o *Operation) SupportsHits() bool { | 202 // SupportsHits checks if a operation supports the hits request. |
180 for _, p := range o.Parameters { | 203 func (op *Operation) SupportsHits() bool { |
204 for _, p := range op.Parameters { | |
181 if p.Name == "resultType" { | 205 if p.Name == "resultType" { |
182 for _, av := range p.AllowedValues.Values { | 206 for _, av := range p.AllowedValues.Values { |
183 if av.Value == "hits" { | 207 if av.Value == "hits" { |
184 return true | 208 return true |
185 } | 209 } |
187 } | 211 } |
188 } | 212 } |
189 return false | 213 return false |
190 } | 214 } |
191 | 215 |
192 func (o *Operation) SupportsOutputFormat(formats ...string) bool { | 216 // SupportsOutputFormat checks if one of the given formats is supported. |
193 for _, p := range o.Parameters { | 217 func (op *Operation) SupportsOutputFormat(formats ...string) bool { |
218 for _, p := range op.Parameters { | |
194 if p.Name == "outputFormat" { | 219 if p.Name == "outputFormat" { |
195 for _, av := range p.AllowedValues.Values { | 220 for _, av := range p.AllowedValues.Values { |
196 for _, f := range formats { | 221 for _, f := range formats { |
197 if av.Value == f { | 222 if av.Value == f { |
198 return true | 223 return true |
202 } | 227 } |
203 } | 228 } |
204 return false | 229 return false |
205 } | 230 } |
206 | 231 |
207 func (o *Operation) FeaturesPerPage() (int, bool) { | 232 // FeaturesPerPage returns the number of features per page. |
208 for _, c := range o.Constraints { | 233 // Returns if paging is not supported by the operation. |
234 func (op *Operation) FeaturesPerPage() (int, bool) { | |
235 for _, c := range op.Constraints { | |
209 if c.Name == "CountDefault" { | 236 if c.Name == "CountDefault" { |
210 if c.DefaultValue != nil { | 237 if c.DefaultValue != nil { |
211 if v, err := strconv.Atoi(c.DefaultValue.Value); err == nil { | 238 if v, err := strconv.Atoi(c.DefaultValue.Value); err == nil { |
212 return v, true | 239 return v, true |
213 } | 240 } |
221 } | 248 } |
222 } | 249 } |
223 return 0, false | 250 return 0, false |
224 } | 251 } |
225 | 252 |
253 // FindFeatureType searches the layers for a given name. | |
254 // Returns nil if not found. | |
226 func (c *Capabilities) FindFeatureType(name string) *FeatureType { | 255 func (c *Capabilities) FindFeatureType(name string) *FeatureType { |
227 for _, ft := range c.FeatureTypeList.FeatureTypes { | 256 for _, ft := range c.FeatureTypeList.FeatureTypes { |
228 if ft.Name == name { | 257 if ft.Name == name { |
229 return ft | 258 return ft |
230 } | 259 } |
231 } | 260 } |
232 return nil | 261 return nil |
233 } | 262 } |
234 | 263 |
264 // FindParameter searches for named parameter. Returns nil | |
265 // if not found. | |
235 func (op *Operation) FindParameter(name string) *Parameter { | 266 func (op *Operation) FindParameter(name string) *Parameter { |
236 for _, p := range op.Parameters { | 267 for _, p := range op.Parameters { |
237 if p.Name == name { | 268 if p.Name == name { |
238 return p | 269 return p |
239 } | 270 } |
240 } | 271 } |
241 return nil | 272 return nil |
242 } | 273 } |
243 | 274 |
244 const WFS2_0_0 = "2.0.0" | 275 // WFS200 is dotted version string of version 2.0.0. |
276 const WFS200 = "2.0.0" | |
245 | 277 |
246 var versionRe = regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`) | 278 var versionRe = regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`) |
247 | 279 |
248 func versionIsLess(a, b string) bool { | 280 func versionIsLess(a, b string) bool { |
249 am := versionRe.FindStringSubmatch(a) | 281 am := versionRe.FindStringSubmatch(a) |
293 } | 325 } |
294 } | 326 } |
295 return a | 327 return a |
296 } | 328 } |
297 | 329 |
330 // HighestWFSVersion figures out the highest supported WFS version. | |
331 // Defaults to def. | |
298 func (c *Capabilities) HighestWFSVersion(def string) string { | 332 func (c *Capabilities) HighestWFSVersion(def string) string { |
299 op := c.FindOperation("GetCapabilities") | 333 op := c.FindOperation("GetCapabilities") |
300 if op == nil { | 334 if op == nil { |
301 return def | 335 return def |
302 } | 336 } |
315 | 349 |
316 return max | 350 return max |
317 } | 351 } |
318 | 352 |
319 var ( | 353 var ( |
354 // ErrInvalidCRS is returned if a given string is not valid CRS URN. | |
320 ErrInvalidCRS = errors.New("Invalid CRS string") | 355 ErrInvalidCRS = errors.New("Invalid CRS string") |
321 crsRe = regexp.MustCompile(`urn:ogc:def:crs:EPSG:[^:]*:(\d+)`) | 356 crsRe = regexp.MustCompile(`urn:ogc:def:crs:EPSG:[^:]*:(\d+)`) |
322 ) | 357 ) |
323 | 358 |
359 // CRSToEPSG extracts the EPSG code from a given CRS URN string. | |
324 func CRSToEPSG(s string) (int, error) { | 360 func CRSToEPSG(s string) (int, error) { |
325 m := crsRe.FindStringSubmatch(s) | 361 m := crsRe.FindStringSubmatch(s) |
326 if m == nil { | 362 if m == nil { |
327 return 0, ErrInvalidCRS | 363 return 0, ErrInvalidCRS |
328 } | 364 } |
329 return strconv.Atoi(m[1]) | 365 return strconv.Atoi(m[1]) |
330 } | 366 } |
331 | 367 |
368 // ParseCapabilities constructs a capabilities document from an io.Reader. | |
332 func ParseCapabilities(r io.Reader) (*Capabilities, error) { | 369 func ParseCapabilities(r io.Reader) (*Capabilities, error) { |
333 | 370 |
334 decoder := xml.NewDecoder(r) | 371 decoder := xml.NewDecoder(r) |
335 decoder.CharsetReader = charset.NewReaderLabel | 372 decoder.CharsetReader = charset.NewReaderLabel |
336 | 373 |