Mercurial > gemma
view pkg/wfs/capabilities.go @ 1608:427f9010b4a9
WFS download: Started with GET downloader (paged and unpaged).
author | Sascha L. Teichmann <sascha.teichmann@intevation.de> |
---|---|
date | Mon, 17 Dec 2018 18:27:57 +0100 |
parents | e80e35b26f17 |
children | efc409e330a6 |
line wrap: on
line source
// This is Free Software under GNU Affero General Public License v >= 3.0 // without warranty, see README.md and license for details. // // SPDX-License-Identifier: AGPL-3.0-or-later // License-Filename: LICENSES/AGPL-3.0.txt // // Copyright (C) 2018 by via donau // – Österreichische Wasserstraßen-Gesellschaft mbH // Software engineering by Intevation GmbH // // Author(s): // * Sascha L. Teichmann <sascha.teichmann@intevation.de> package wfs import ( "encoding/xml" "io" "regexp" "strconv" "golang.org/x/net/html/charset" ) type Keyword struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keyword"` Value string `xml:",cdata"` } type Keywords struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keywords"` Keywords []Keyword `xml:"Keyword"` } type ServiceIdentification struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 ServiceIdentification"` Title string Abstract string Keywords Keywords `xml:"Keywords"` ServiceType string ServiceTypeVersion string } type Get struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Get"` HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` } type Post struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Post"` HRef string `xml:"http://www.w3.org/1999/xlink href,attr"` } type HTTP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 HTTP"` Get *Get `xml:"Get"` Post *Post `xml:"Post"` } type DCP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DCP"` HTTP HTTP `xml:"HTTP"` } type Value struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Value"` Value string `xml:",cdata"` } type AllowedValues struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 AllowedValues"` Values []Value `xml:"Value"` } type Parameter struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Parameter"` Name string `xml:"name,attr"` AllowedValues AllowedValues `xml:"AllowedValues"` } type DefaultValue struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DefaultValue"` Value string `xml:",cdata"` } type Constraint struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Constraint"` Name string `xml:"name,attr"` AllowedValues AllowedValues `xml:"AllowedValues"` DefaultValue *DefaultValue `xml:"DefaultValue"` } type Operation struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Operation"` Name string `xml:"name,attr"` DCP DCP `xml:"DCP"` Parameters []*Parameter `xml:"Parameter"` Constraints []*Constraint `xml:"Constraint"` } type OperationsMetadata struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 OperationsMetadata"` Operations []*Operation `xml:"Operation"` Constraints []*Constraint `xml:"Constraint"` } type WGS84BoundingBox struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 WGS84BoundingBox"` LowerCorner string `xml:"LowerCorner"` UpperCorner string `xml:"UpperCorner"` } type FeatureType struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureType"` Name string `xml:"Name"` Title string `xml:"Title"` Abstract string `xml:"Abstract"` Keywords Keywords `xml:"Keywords"` DefaultCRS string `xml:"DefaultCRS"` OtherCRSs []string `xml:"OtherCRS"` WGS84BoundingBox *WGS84BoundingBox `xml:"WGS84BoundingBox"` Namespaces []xml.Name `xml:"-"` } // shadowFeatureType is used to prevent recursive UnmarshalXML for FeatureType. type shadowFeatureType struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureType"` Name string `xml:"Name"` Title string `xml:"Title"` Abstract string `xml:"Abstract"` Keywords Keywords `xml:"Keywords"` DefaultCRS string `xml:"DefaultCRS"` OtherCRSs []string `xml:"OtherCRS"` WGS84BoundingBox *WGS84BoundingBox `xml:"WGS84BoundingBox"` Namespaces []xml.Name `xml:"-"` } func (ft *FeatureType) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { // Filter out the namespaces for this feature type. var ns []xml.Name for _, attr := range start.Attr { if attr.Name.Space == "xmlns" { ns = append(ns, xml.Name{Space: attr.Name.Local, Local: attr.Value}) } } var sft shadowFeatureType if err := d.DecodeElement(&sft, &start); err != nil { return err } *ft = FeatureType(sft) ft.Namespaces = ns return nil } type FeatureTypeList struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureTypeList"` FeatureTypes []*FeatureType `xml:"FeatureType"` } type Capabilities struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 WFS_Capabilities"` BaseURL string `xml:"-"` ServiceIdentification ServiceIdentification OperationsMetadata OperationsMetadata FeatureTypeList FeatureTypeList } func (c *Capabilities) FindOperation(name string) *Operation { for _, op := range c.OperationsMetadata.Operations { if op.Name == name { return op } } return nil } func (o *Operation) SupportsHits() bool { for _, p := range o.Parameters { if p.Name == "resultType" { for _, av := range p.AllowedValues.Values { if av.Value == "hits" { return true } } } } return false } func (o *Operation) FeaturesPerPage() (int, bool) { for _, c := range o.Constraints { if c.Name == "CountDefault" { if c.DefaultValue != nil { if v, err := strconv.Atoi(c.DefaultValue.Value); err == nil { return v, true } } for _, av := range c.AllowedValues.Values { if v, err := strconv.Atoi(av.Value); err == nil { return v, true } } } } return 0, false } func (c *Capabilities) FindFeatureType(name string) *FeatureType { for _, ft := range c.FeatureTypeList.FeatureTypes { if ft.Name == name { return ft } } return nil } func (op *Operation) FindParameter(name string) *Parameter { for _, p := range op.Parameters { if p.Name == name { return p } } return nil } const WFS2_0_0 = "2.0.0" var versionRe = regexp.MustCompile(`(\d+)\.(\d+)\.(\d+)`) func versionIsLess(a, b string) bool { am := versionRe.FindStringSubmatch(a) bm := versionRe.FindStringSubmatch(b) var n int if len(am) < len(bm) { n = len(am) } else { n = len(bm) } n-- for i := 0; i < n; i++ { ai, _ := strconv.Atoi(am[i+1]) bi, _ := strconv.Atoi(bm[i+1]) switch { case ai < bi: return true case ai > bi: return false } } return false } func maxVersion(a, b string) string { am := versionRe.FindStringSubmatch(a) bm := versionRe.FindStringSubmatch(b) var n int if len(am) < len(bm) { n = len(am) } else { n = len(bm) } n-- for i := 0; i < n; i++ { ai, _ := strconv.Atoi(am[i+1]) bi, _ := strconv.Atoi(bm[i+1]) switch { case ai > bi: return a case bi > ai: return b } } return a } func (c *Capabilities) HighestWFSVersion(def string) string { op := c.FindOperation("GetCapabilities") if op == nil { return def } p := op.FindParameter("AcceptVersions") if p == nil { return def } if len(p.AllowedValues.Values) == 0 { return def } max := p.AllowedValues.Values[0].Value for _, v := range p.AllowedValues.Values[1:] { max = maxVersion(max, v.Value) } return max } func ParseCapabilities(r io.Reader) (*Capabilities, error) { decoder := xml.NewDecoder(r) decoder.CharsetReader = charset.NewReaderLabel var capabilities Capabilities if err := decoder.Decode(&capabilities); err != nil { return nil, err } return &capabilities, nil }