view pkg/wfs/capabilities.go @ 1602:e80e35b26f17

WFS Capabilities parser: Wrote a custom xml.Unmarshaler to extract the namespaces of FeatureTypes, too. They are needed in GetFeature requests.
author Sascha L. Teichmann <sascha.teichmann@intevation.de>
date Mon, 17 Dec 2018 13:01:58 +0100
parents 658c1ebc1707
children 427f9010b4a9
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"

	"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"`

	ServiceIdentification ServiceIdentification
	OperationsMetadata    OperationsMetadata
	FeatureTypeList       FeatureTypeList
}

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
}