From 0f6d250b986990cb619fd20869868f8f88dbe189 Mon Sep 17 00:00:00 2001 From: Roel van den Berg Date: Fri, 3 Sep 2021 12:59:12 +0200 Subject: [PATCH] Fixes for validation errors. --- pkg/wfs200/capabilities.go | 12 +++---- pkg/wfs200/crs.go | 44 +++++++++++++++++++++++ pkg/wfs200/crs_test.go | 60 ++++++++++++++++++++++++++++++++ pkg/wfs200/crs_xml.go | 36 +++++++++++++++++++ pkg/wfs200/crs_yaml.go | 16 +++++++++ pkg/wmts100/capabilities_test.go | 4 +-- pkg/wsc110/keywords.go | 7 ++-- 7 files changed, 169 insertions(+), 10 deletions(-) create mode 100644 pkg/wfs200/crs.go create mode 100644 pkg/wfs200/crs_test.go create mode 100644 pkg/wfs200/crs_xml.go create mode 100644 pkg/wfs200/crs_yaml.go diff --git a/pkg/wfs200/capabilities.go b/pkg/wfs200/capabilities.go index d09e083..ea36d12 100644 --- a/pkg/wfs200/capabilities.go +++ b/pkg/wfs200/capabilities.go @@ -123,12 +123,12 @@ type FeatureTypeList struct { // FeatureType struct for the WFS 2.0.0 type FeatureType struct { - Name string `xml:"Name" yaml:"name"` - Title string `xml:"Title" yaml:"title"` - Abstract string `xml:"Abstract" yaml:"abstract"` - Keywords *wsc110.Keywords `xml:"ows:Keywords" yaml:"keywords"` - DefaultCRS *wsc110.CRS `xml:"DefaultCRS" yaml:"defaultcrs"` - OtherCRS *[]wsc110.CRS `xml:"OtherCRS" yaml:"othercrs"` + Name string `xml:"Name" yaml:"name"` + Title string `xml:"Title" yaml:"title"` + Abstract string `xml:"Abstract" yaml:"abstract"` + Keywords *[]wsc110.Keywords `xml:"ows:Keywords" yaml:"keywords"` + DefaultCRS *CRS `xml:"DefaultCRS" yaml:"defaultcrs"` + OtherCRS *[]CRS `xml:"OtherCRS" yaml:"othercrs"` OutputFormats struct { Format []string `xml:"Format" yaml:"format"` } `xml:"OutputFormats" yaml:"outputformats"` diff --git a/pkg/wfs200/crs.go b/pkg/wfs200/crs.go new file mode 100644 index 0000000..52a2693 --- /dev/null +++ b/pkg/wfs200/crs.go @@ -0,0 +1,44 @@ +package wfs200 + +import ( + "regexp" + "strconv" +) + +// +const ( + codeSpace = `urn:ogc:def:crs:EPSG:` +) + +// CRS struct with namespace/authority/registry and code +type CRS struct { + Namespace string //TODO maybe AuthorityType is a better name...? + Code int +} + +// String of the EPSGCode +func (c *CRS) String() string { + return c.Namespace + `:` + strconv.Itoa(c.Code) +} + +// Identifier returns the EPSG +func (c *CRS) Identifier() string { + return codeSpace + strconv.Itoa(c.Code) +} + +// ParseString build CRS struct from input string +func (c *CRS) ParseString(s string) { + c.parseString(s) +} + +func (c *CRS) parseString(s string) { + regex := regexp.MustCompile(`(^.*):([0-9]+)`) + code := regex.FindStringSubmatch(s) + if len(code) == 3 { // code[0] is the full match, the other the parts + c.Namespace = codeSpace + + // the regex already checks if it [0-9] + i, _ := strconv.Atoi(code[2]) + c.Code = i + } +} diff --git a/pkg/wfs200/crs_test.go b/pkg/wfs200/crs_test.go new file mode 100644 index 0000000..9175737 --- /dev/null +++ b/pkg/wfs200/crs_test.go @@ -0,0 +1,60 @@ +package wfs200 + +import ( + "testing" + + "gopkg.in/yaml.v3" +) + +func TestCRSParseString(t *testing.T) { + var tests = []struct { + input string + expectedCRS CRS + }{ + 0: {}, // Empty input == empty struct + 1: {input: `urn:ogc:def:crs:EPSG::4326`, expectedCRS: CRS{Code: 4326, Namespace: `urn:ogc:def:crs:EPSG:`}}, + 2: {input: `EPSG:4326`, expectedCRS: CRS{Code: 4326, Namespace: `urn:ogc:def:crs:EPSG:`}}, + } + + for k, test := range tests { + var crs CRS + crs.parseString(test.input) + + if crs.Code != test.expectedCRS.Code || crs.Namespace != test.expectedCRS.Namespace { + t.Errorf("test: %d, expected: %v,\n got: %v", k, test.expectedCRS, crs) + } + } +} + +func TestUnmarshalYAMLCrs(t *testing.T) { + + var stringYAML = `defaultcrs: urn:ogc:def:crs:EPSG::4326 +othercrs: +- EPSG:4258 +- urn:ogc:def:crs:EPSG::3857` + + type FeatureType struct { + DefaultCRS CRS `yaml:"defaultcrs"` + OtherCRS []CRS `yaml:"othercrs"` + } + + var tests = []struct { + yaml []byte + expectedcrs CRS + }{ + 0: {yaml: []byte(stringYAML), expectedcrs: CRS{Code: 4326, Namespace: codeSpace}}, + 1: {yaml: []byte(`defaultcrs: urn:ogc:def:crs:EPSG::4326`), expectedcrs: CRS{Code: 4326, Namespace: codeSpace}}, + 2: {yaml: []byte(`defaultcrs: EPSG:4326`), expectedcrs: CRS{Code: 4326, Namespace: codeSpace}}, + } + for k, test := range tests { + var ftl FeatureType + err := yaml.Unmarshal(test.yaml, &ftl) + if err != nil { + t.Errorf("test: %d, yaml.UnMarshal failed with '%s'\n", k, err) + } else { + if ftl.DefaultCRS.Code != test.expectedcrs.Code || ftl.DefaultCRS.Namespace != test.expectedcrs.Namespace { + t.Errorf("test: %d, expected: %v+,\n got: %v+", k, test.expectedcrs, ftl.DefaultCRS) + } + } + } +} diff --git a/pkg/wfs200/crs_xml.go b/pkg/wfs200/crs_xml.go new file mode 100644 index 0000000..9238966 --- /dev/null +++ b/pkg/wfs200/crs_xml.go @@ -0,0 +1,36 @@ +package wfs200 + +import ( + "encoding/xml" + "fmt" +) + +// MarshalXML Position +func (c *CRS) MarshalXML(e *xml.Encoder, start xml.StartElement) error { + var s = `` + if c.Namespace != `` { + s = fmt.Sprintf("%s:%d", c.Namespace, c.Code) + } + + return e.EncodeElement(s, start) +} + +// UnmarshalXML Position +func (c *CRS) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error { + var crs CRS + for { + token, err := d.Token() + if err != nil { + return err + } + switch el := token.(type) { + case xml.CharData: + crs.parseString(string([]byte(el))) + case xml.EndElement: + if el == start.End() { + *c = crs + return nil + } + } + } +} diff --git a/pkg/wfs200/crs_yaml.go b/pkg/wfs200/crs_yaml.go new file mode 100644 index 0000000..f040a09 --- /dev/null +++ b/pkg/wfs200/crs_yaml.go @@ -0,0 +1,16 @@ +package wfs200 + +// UnmarshalYAML CRS +func (c *CRS) UnmarshalYAML(unmarshal func(interface{}) error) error { + var s string + if err := unmarshal(&s); err != nil { + return err + } + + var crs CRS + crs.parseString(s) + + *c = crs + + return nil +} diff --git a/pkg/wmts100/capabilities_test.go b/pkg/wmts100/capabilities_test.go index 47936c7..9c4cc76 100644 --- a/pkg/wmts100/capabilities_test.go +++ b/pkg/wmts100/capabilities_test.go @@ -51,8 +51,8 @@ var contentsWithLegend = Contents{ func TestBuildStyleWithLegend(t *testing.T) { expected := `` output, _ := xml.MarshalIndent(contentsWithLegend.Layer[0].Style, "", " ") @@ -91,8 +91,8 @@ var contentsWithoutLegend = Contents{ func TestBuildStyleWithoutLegend(t *testing.T) { expected := `` output, _ := xml.MarshalIndent(contentsWithoutLegend.Layer[0].Style, "", " ") if string(output) != expected { diff --git a/pkg/wsc110/keywords.go b/pkg/wsc110/keywords.go index 3077915..7eeb266 100644 --- a/pkg/wsc110/keywords.go +++ b/pkg/wsc110/keywords.go @@ -2,6 +2,9 @@ package wsc110 // Keywords in struct for repeatability type Keywords struct { - Keyword []string `xml:"ows:Keyword" yaml:"keyword"` - Type string `xml:"ows:Type,omitempty" yaml:"type"` + Keyword []string `xml:"ows:Keyword"` + Type *struct { + Text string `xml:",chardata"` + CodeSpace *string `xml:"codeSpace,attr,omitempty"` + } `xml:"ows:Type"` }