comparison pkg/wfs/download.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
children efc409e330a6
comparison
equal deleted inserted replaced
1607:38f91897ca69 1608:427f9010b4a9
1 // This is Free Software under GNU Affero General Public License v >= 3.0
2 // without warranty, see README.md and license for details.
3 //
4 // SPDX-License-Identifier: AGPL-3.0-or-later
5 // License-Filename: LICENSES/AGPL-3.0.txt
6 //
7 // Copyright (C) 2018 by via donau
8 // – Österreichische Wasserstraßen-Gesellschaft mbH
9 // Software engineering by Intevation GmbH
10 //
11 // Author(s):
12 // * Sascha L. Teichmann <sascha.teichmann@intevation.de>
13
14 package wfs
15
16 import (
17 "bufio"
18 "encoding/xml"
19 "errors"
20 "log"
21 "net/http"
22 "net/url"
23 "strconv"
24
25 "golang.org/x/net/html/charset"
26 )
27
28 var (
29 ErrNoSuchFeatureType = errors.New("No such feature type")
30 ErrGetFeatureNotSupported = errors.New("GetFeature not supported")
31 ErrMethodGetNotSupported = errors.New("GET not supported")
32 ErrNoNumberMatchedFound = errors.New("No numberMatched attribute found")
33 )
34
35 func GetCapabilities(capURL string) (*Capabilities, error) {
36
37 base, err := url.Parse(capURL)
38 if err != nil {
39 return nil, err
40 }
41 v := url.Values{}
42 v.Set("SERVICE", "WFS")
43 v.Set("REQUEST", "GetCapabilities")
44 v.Set("ACCEPTVERSIONS", "2.0.0,1.1.0,1.0.0")
45 base.RawQuery = v.Encode()
46
47 baseURL := base.String()
48 resp, err := http.Get(baseURL)
49 if err != nil {
50 return nil, err
51 }
52 defer resp.Body.Close()
53 caps, err := ParseCapabilities(bufio.NewReader(resp.Body))
54 if err == nil {
55 caps.BaseURL = baseURL
56 }
57 return caps, err
58 }
59
60 func numberFeaturesGET(u *url.URL, featureType, version string) (int, error) {
61
62 v := url.Values{}
63 v.Set("SERVICE", "WFS")
64 v.Set("REQUEST", "GetFeature")
65 v.Set("resultType", "hits")
66 v.Set("VERSION", version)
67 v.Set("TYPENAMES", featureType)
68
69 q := *u
70 q.RawQuery = v.Encode()
71
72 resp, err := http.Get(q.String())
73 if err != nil {
74 return 0, err
75 }
76 defer resp.Body.Close()
77 dec := xml.NewDecoder(resp.Body)
78 dec.CharsetReader = charset.NewReaderLabel
79
80 var result struct {
81 NumberMatched *int `xml:"numberMatched,attr"`
82 }
83
84 if err := dec.Decode(&result); err != nil {
85 return 0, err
86 }
87
88 if result.NumberMatched == nil {
89 return 0, ErrNoNumberMatchedFound
90 }
91
92 return *result.NumberMatched, nil
93 }
94
95 func GetFeaturesGET(caps *Capabilities, featureTypeName string) error {
96
97 if caps.FindFeatureType(featureTypeName) == nil {
98 return ErrNoSuchFeatureType
99 }
100 op := caps.FindOperation("GetFeature")
101 if op == nil {
102 return ErrGetFeatureNotSupported
103 }
104
105 if op.DCP.HTTP.Get == nil {
106 return ErrMethodGetNotSupported
107 }
108
109 getRaw := op.DCP.HTTP.Get.HRef
110 getU, err := url.Parse(getRaw)
111 if err != nil {
112 return err
113 }
114 // The URL could be relative so resolve against Capabilities URL.
115 if !getU.IsAbs() {
116 base, err := url.Parse(caps.BaseURL)
117 if err != nil {
118 return err
119 }
120 getU = getU.ResolveReference(base)
121 }
122
123 wfsVersion := caps.HighestWFSVersion(WFS2_0_0)
124
125 featuresPerPage, supportsPaging := op.FeaturesPerPage()
126
127 var numFeatures int
128
129 if supportsPaging {
130 log.Printf("Paging supported with %d feature per page.\n",
131 featuresPerPage)
132
133 if !op.SupportsHits() {
134 supportsPaging = false
135 } else {
136 numFeatures, err = numberFeaturesGET(getU, featureTypeName, wfsVersion)
137 if err != nil {
138 log.Printf("error: %v\n", err)
139 supportsPaging = false
140 } else {
141 log.Printf("Number of features: %d\n", numFeatures)
142 }
143 }
144 }
145
146 var downloadURLs []string
147
148 if supportsPaging {
149 wfs2 := !versionIsLess(wfsVersion, WFS2_0_0)
150 pagedURL := func(ofs, count int) string {
151 v := url.Values{}
152 v.Set("SERVICE", "WFS")
153 v.Set("REQUEST", "GetFeature")
154 v.Set("VERSION", wfsVersion)
155 v.Set("startIndex", strconv.Itoa(ofs))
156 if wfs2 {
157 v.Set("count", strconv.Itoa(count))
158 } else {
159 v.Set("maxFeatures", strconv.Itoa(count))
160 }
161 v.Set("TYPENAMES", featureTypeName)
162 q := *getU
163 q.RawQuery = v.Encode()
164 return q.String()
165 }
166 if numFeatures <= featuresPerPage {
167 log.Println("All features can be fetched in one page")
168 downloadURLs = []string{pagedURL(0, numFeatures)}
169 } else {
170 log.Println("Features need to be downloaded in pages.")
171 for pos := 0; pos < numFeatures; {
172 var count int
173 if rest := numFeatures - pos; rest >= numFeatures {
174 count = numFeatures
175 } else {
176 count = rest
177 }
178 downloadURLs = append(downloadURLs, pagedURL(pos, count))
179 pos += count
180 }
181 }
182 } else { // No paging support.
183 v := url.Values{}
184 v.Set("SERVICE", "WFS")
185 v.Set("REQUEST", "GetFeature")
186 v.Set("VERSION", wfsVersion)
187 v.Set("TYPENAMES", featureTypeName)
188 q := *getU
189 q.RawQuery = v.Encode()
190 downloadURLs = []string{q.String()}
191 }
192
193 // TODO: Implement me!
194
195 log.Printf("%v\n", downloadURLs)
196
197 return nil
198 }