Mercurial > gemma
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 } |