Mercurial > gemma
view pkg/wfs/capabilities.go @ 2130:f3aabc05f9b2
Fix constraints on waterway profiles
staging_done in the UNIQUE constraint had no effect, because the
exclusion constraint prevented two rows with equal location and
validity anyhow. Adding staging_done to the exclusion constraint
makes the UNIQUE constraint checking only a corner case of what
the exclusion constraint checks. Thus, remove the UNIQUE constraint.
Casting staging_done to int is needed because there is no appropriate
operator class for booleans. Casting to smallint or even bit would have
been better (i.e. should result in smaller index size), but that would
have required creating such a CAST, in addition.
author | Tom Gottfried <tom@intevation.de> |
---|---|
date | Wed, 06 Feb 2019 15:42:32 +0100 |
parents | 6caf5cd6249e |
children | 02505fcff63c |
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" "errors" "io" "regexp" "strconv" "golang.org/x/net/html/charset" ) // Keyword stores a value. type Keyword struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keyword"` Value string `xml:",cdata"` } // Keywords stores a list of keywords. type Keywords struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Keywords"` Keywords []Keyword `xml:"Keyword"` } // ServiceIdentification contains meta informations about a WFS. 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 } // Get stores the link to the GET method 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"` } // Post stores the link to the POST method. 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"` } // HTTP is a container for HTTP methods. type HTTP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 HTTP"` Get *Get `xml:"Get"` Post *Post `xml:"Post"` } // DCP wraps the HTTP container. type DCP struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DCP"` HTTP HTTP `xml:"HTTP"` } // Value is a simple string value. type Value struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Value"` Value string `xml:",cdata"` } // AllowedValues is list positive list of values. type AllowedValues struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 AllowedValues"` Values []Value `xml:"Value"` } // Parameter is a named parameter with a list of allowed values. type Parameter struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 Parameter"` Name string `xml:"name,attr"` AllowedValues AllowedValues `xml:"AllowedValues"` } // DefaultValue is the default value of a constraint. type DefaultValue struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 DefaultValue"` Value string `xml:",cdata"` } // Constraint is a named constraint with a list of allowed values // and a default value. 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"` } // Operation contains informations of a WFS operation. 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"` } // OperationsMetadata is list of operations and constraints. type OperationsMetadata struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 OperationsMetadata"` Operations []*Operation `xml:"Operation"` Constraints []*Constraint `xml:"Constraint"` } // WGS84BoundingBox is a bounding box feature type in WGS84. type WGS84BoundingBox struct { XMLName xml.Name `xml:"http://www.opengis.net/ows/1.1 WGS84BoundingBox"` LowerCorner string `xml:"LowerCorner"` UpperCorner string `xml:"UpperCorner"` } // FeatureType is layer served by the WFS: 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:"-"` } // UnmarshalXML implements xml.Unmarshaler for better namespace handling. 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 } // FeatureTypeList is the list of layers served by the WFS. type FeatureTypeList struct { XMLName xml.Name `xml:"http://www.opengis.net/wfs/2.0 FeatureTypeList"` FeatureTypes []*FeatureType `xml:"FeatureType"` } // Capabilities is the top level metadata struct. 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 } // FindOperation searches the capabilities for a specifc operation. // Returns nil if not found. func (c *Capabilities) FindOperation(name string) *Operation { for _, op := range c.OperationsMetadata.Operations { if op.Name == name { return op } } return nil } // SupportsHits checks if a operation supports the hits request. func (op *Operation) SupportsHits() bool { for _, p := range op.Parameters { if p.Name == "resultType" { for _, av := range p.AllowedValues.Values { if av.Value == "hits" { return true } } } } return false } // SupportsOutputFormat checks if one of the given formats is supported. func (op *Operation) SupportsOutputFormat(formats ...string) bool { for _, p := range op.Parameters { if p.Name == "outputFormat" { for _, av := range p.AllowedValues.Values { for _, f := range formats { if av.Value == f { return true } } } } } return false } // FeaturesPerPage returns the number of features per page. // Returns if paging is not supported by the operation. func (op *Operation) FeaturesPerPage() (int, bool) { for _, c := range op.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 } // FindFeatureType searches the layers for a given name. // Returns nil if not found. func (c *Capabilities) FindFeatureType(name string) *FeatureType { for _, ft := range c.FeatureTypeList.FeatureTypes { if ft.Name == name { return ft } } return nil } // FindParameter searches for named parameter. Returns nil // if not found. func (op *Operation) FindParameter(name string) *Parameter { for _, p := range op.Parameters { if p.Name == name { return p } } return nil } // WFS200 is dotted version string of version 2.0.0. const WFS200 = "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 } // HighestWFSVersion figures out the highest supported WFS version. // Defaults to def. 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 } var ( // ErrInvalidCRS is returned if a given string is not valid CRS URN. ErrInvalidCRS = errors.New("Invalid CRS string") crsRe = regexp.MustCompile(`urn:ogc:def:crs:EPSG:[^:]*:(\d+)`) ) // CRSToEPSG extracts the EPSG code from a given CRS URN string. func CRSToEPSG(s string) (int, error) { m := crsRe.FindStringSubmatch(s) if m == nil { return 0, ErrInvalidCRS } return strconv.Atoi(m[1]) } // ParseCapabilities constructs a capabilities document from an io.Reader. 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 }