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