|
1 | 1 | package atom_generator |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "fmt" |
| 5 | + "github.com/cbroglie/mustache" |
4 | 6 | atom_feed "github.com/pdok/atom-generator/feeds" |
5 | 7 | pdoknlv3 "github.com/pdok/atom-operator/api/v3" |
6 | 8 | v1 "github.com/pdok/smooth-operator/api/v1" |
| 9 | + "strings" |
| 10 | + "time" |
7 | 11 | ) |
8 | 12 |
|
9 | 13 | func MapAtomV3ToAtomGeneratorConfig(atom pdoknlv3.Atom, ownerInfo v1.OwnerInfo) (atomGeneratorConfig atom_feed.Feeds, err error) { |
10 | 14 |
|
11 | | - lang := "nl" |
| 15 | + var describedbyLink, searchLink, relatedLink atom_feed.Link |
12 | 16 |
|
13 | | - // Todo use these templates |
14 | | - // csv := ownerInfo.Spec.MetadataUrls.CSV |
15 | | - // opensearch := ownerInfo.Spec.MetadataUrls.OpenSearch |
16 | | - // html := ownerInfo.Spec.MetadataUrls.HTML |
| 17 | + language := "nl" |
| 18 | + xmlSheet := "https://service.pdok.nl/atom/style/style.xsl" |
| 19 | + selfLink := getSelfLink(atom, language) |
| 20 | + describedbyLink, err = getCSWDescribedbyLink(atom, language, ownerInfo) |
| 21 | + if err != nil { |
| 22 | + return atom_feed.Feeds{}, err |
| 23 | + } |
| 24 | + searchLink, err = getSearchLink(atom, language, ownerInfo) |
| 25 | + if err != nil { |
| 26 | + return atom_feed.Feeds{}, err |
| 27 | + } |
| 28 | + relatedLink, err = getHTMLRelatedLink(atom, language, ownerInfo) |
| 29 | + latestUpdated := getLatestUpdate(atom.Spec.DatasetFeeds) |
17 | 30 |
|
18 | 31 | atomGeneratorConfig = atom_feed.Feeds{ |
19 | 32 | Feeds: []atom_feed.Feed{ |
20 | 33 | { |
21 | | - ID: atom.Spec.Service.BaseURL + "/index.xml", |
22 | | - InspireDls: "http://inspire.ec.europa.eu/schemas/inspire_dls/1.0", |
23 | | - Lang: &lang, |
24 | | - // XMLStylesheet: "", |
| 34 | + //XMLName: Name{"http://www.w3.org/2005/Atom", "feed"}, |
| 35 | + XMLStylesheet: &xmlSheet, |
| 36 | + Xmlns: "http://www.w3.org/2005/Atom", |
| 37 | + Georss: "http://www.georss.org/georss", |
| 38 | + InspireDls: "http://inspire.ec.europa.eu/schemas/inspire_dls/1.0", |
| 39 | + Lang: &language, |
| 40 | + ID: atom.Spec.Service.BaseURL + "/index.xml", |
| 41 | + Title: atom.Spec.Service.Title, |
| 42 | + Subtitle: atom.Spec.Service.Subtitle, |
| 43 | + // Feed Links |
| 44 | + Self: &selfLink, |
| 45 | + Describedby: &describedbyLink, |
| 46 | + Search: &searchLink, |
| 47 | + Link: []atom_feed.Link{ |
| 48 | + relatedLink, |
| 49 | + }, |
| 50 | + Rights: atom.Spec.Service.Rights, |
| 51 | + Updated: &latestUpdated, |
| 52 | + Author: getAuthor(atom.Spec.Service.Author), |
| 53 | + Entry: getEntriesArray(atom, ownerInfo), |
25 | 54 | }, |
26 | 55 | }, |
27 | 56 | } |
28 | | - |
29 | | - // atomGeneratorConfig = AtomGeneratorConfig{ |
30 | | - // Feeds: []Feed{ |
31 | | - // { |
32 | | - // ID: atom.Spec.Service.BaseURL + "/index.xml", |
33 | | - // InspireDLS: "http://inspire.ec.europa.eu/schemas/inspire_dls/1.0", |
34 | | - // Lang: "nl", |
35 | | - // Stylesheet: "example.com/styles/atom.xsl", |
36 | | - // Title: "Service Title", |
37 | | - // Subtitle: "Service Subtitle", |
38 | | - // Link: []Link{ |
39 | | - // { |
40 | | - // Rel: "self", |
41 | | - // Href: atom.Spec.Service.BaseURL + "/index.xml", |
42 | | - // Title: "Service Title", |
43 | | - // Type: "application/atom+xml", |
44 | | - // }, |
45 | | - // { |
46 | | - // Rel: "describedby", |
47 | | - // Href: "example.com/getrecord?id=service1", |
48 | | - // Type: "application/xml", |
49 | | - // }, |
50 | | - // { |
51 | | - // Rel: "search", |
52 | | - // Href: "example.com/opensearch.xml", |
53 | | - // Title: "Open Search document voor INSPIRE Download service PDOK", |
54 | | - // Type: "application/opensearchdescription+xml", |
55 | | - // }, |
56 | | - // { |
57 | | - // Rel: "related", |
58 | | - // Href: "example.com/metadata/service1", |
59 | | - // Type: "text/html", |
60 | | - // Title: "NGR pagina voor deze download service", |
61 | | - // }, |
62 | | - // }, |
63 | | - // Rights: "All rights reserved", |
64 | | - // Updated: &updatedTime, |
65 | | - // Author: Author{ |
66 | | - // Name: "PDOK Beheer", |
67 | | - |
68 | | - // }, |
69 | | - // Entry: []Entry{ |
70 | | - // { |
71 | | - // ID: "example.com/atom/dataset1.xml", |
72 | | - // Title: "Dataset 1 Title", |
73 | | - // SpatialDatasetIdentifierCode: "dataset1-id", |
74 | | - // SpatialDatasetIdentifierNamespace: "http://www.pdok.nl", |
75 | | - // Link: []Link{ |
76 | | - // { |
77 | | - // Rel: "describedby", |
78 | | - // Href: "example.com/getrecord?id=dataset1", |
79 | | - // Type: "application/xml", |
80 | | - // }, |
81 | | - // { |
82 | | - // Rel: "alternate", |
83 | | - // Href: "example.com/atom/dataset1.xml", |
84 | | - // Type: "application/atom+xml", |
85 | | - // Title: "Dataset 1 Title", |
86 | | - // }, |
87 | | - // }, |
88 | | - // Updated: &updatedTime, |
89 | | - // Summary: "Dataset 1 Subtitle", |
90 | | - // Polygon: "42.0 12.0 42.0 13.0 43.0 13.0 43.0 12.0 42.0 12.0", |
91 | | - // Category: []Category{ |
92 | | - // { |
93 | | - // Term: "urn:ogc:def:crs:EPSG::4326", |
94 | | - // Label: "EPSG:4326", |
95 | | - // }, |
96 | | - // }, |
97 | | - // }, |
98 | | - // // Adding another entry for completeness |
99 | | - // { |
100 | | - // ID: "example.com/atom/dataset2.xml", |
101 | | - // Title: "Dataset 2 Title", |
102 | | - // Link: []Link{ |
103 | | - // { |
104 | | - // Rel: "self", |
105 | | - // Href: "example.com/atom/dataset2.xml", |
106 | | - // }, |
107 | | - // { |
108 | | - // Rel: "up", |
109 | | - // Href: "example.com/atom/index.xml", |
110 | | - // Type: "application/atom+xml", |
111 | | - // Title: "Top Atom Download Service Feed", |
112 | | - // }, |
113 | | - // { |
114 | | - // Rel: "describedby", |
115 | | - // Href: "example.com/getrecord?id=service1", |
116 | | - // Type: "text/html", |
117 | | - // }, |
118 | | - // { |
119 | | - // Rel: "related", |
120 | | - // Href: "example.com/metadata/dataset2", |
121 | | - // Type: "text/html", |
122 | | - // Title: "NGR pagina voor deze dataset", |
123 | | - // }, |
124 | | - // { |
125 | | - // Rel: "describedby", |
126 | | - // Href: "example.com/link1", |
127 | | - // Title: "Link Type 1", |
128 | | - // Type: "application/pdf", |
129 | | - // Hreflang: "en", |
130 | | - // }, |
131 | | - // // Add more links if needed |
132 | | - // }, |
133 | | - // Rights: "All rights reserved", |
134 | | - // Updated: "20-04-2024 huplelepup", |
135 | | - // }, |
136 | | - // }, |
137 | | - // }, |
138 | | - // }, |
139 | | - // } |
140 | | - |
141 | | - // return |
142 | 57 | return atomGeneratorConfig, err |
143 | 58 | } |
| 59 | + |
| 60 | +func getLatestUpdate(feeds []pdoknlv3.DatasetFeed) string { |
| 61 | + updateTime := feeds[0].Entries[0].Updated |
| 62 | + for _, datasetFeed := range feeds { |
| 63 | + for _, entry := range datasetFeed.Entries { |
| 64 | + if updateTime.Before(entry.Updated) { |
| 65 | + updateTime = entry.Updated |
| 66 | + } |
| 67 | + } |
| 68 | + } |
| 69 | + return updateTime.Format(time.RFC3339) |
| 70 | +} |
| 71 | + |
| 72 | +func getEntriesArray(atom pdoknlv3.Atom, ownerInfo v1.OwnerInfo) []atom_feed.Entry { |
| 73 | + var retEntriesArray []atom_feed.Entry |
| 74 | + for _, datasetFeed := range atom.Spec.DatasetFeeds { |
| 75 | + for _, entry := range datasetFeed.Entries { |
| 76 | + updateTime := entry.Updated.Format(time.RFC3339) |
| 77 | + |
| 78 | + singleEntry := atom_feed.Entry{ |
| 79 | + ID: entry.TechnicalName, |
| 80 | + Title: entry.Title, |
| 81 | + Content: entry.Content, |
| 82 | + Summary: datasetFeed.Subtitle, |
| 83 | + Rights: atom.Spec.Service.Rights, |
| 84 | + Updated: &updateTime, |
| 85 | + Polygon: getBoundingBoxPolygon(entry.Polygon.BBox), |
| 86 | + SpatialDatasetIdentifierCode: datasetFeed.SpatialDatasetIdentifierCode, |
| 87 | + SpatialDatasetIdentifierNamespace: datasetFeed.SpatialDatasetIdentifierNamespace, |
| 88 | + Category: getCategory(entry.SRS), |
| 89 | + Link: getEntryLinksArray(entry), |
| 90 | + } |
| 91 | + retEntriesArray = append(retEntriesArray, singleEntry) |
| 92 | + } |
| 93 | + } |
| 94 | + |
| 95 | + return retEntriesArray |
| 96 | +} |
| 97 | + |
| 98 | +func getEntryLinksArray(entry pdoknlv3.Entry) []atom_feed.Link { |
| 99 | + linksArray := []atom_feed.Link{} |
| 100 | + for _, link := range entry.DownloadLinks { |
| 101 | + dataLink := link.Data |
| 102 | + bboxString := getBboxString(link.BBox) |
| 103 | + |
| 104 | + l := atom_feed.Link{ |
| 105 | + Data: &dataLink, |
| 106 | + Rel: link.Rel, |
| 107 | + Version: link.Version, |
| 108 | + Time: link.Time, |
| 109 | + Bbox: &bboxString, |
| 110 | + } |
| 111 | + linksArray = append(linksArray, l) |
| 112 | + } |
| 113 | + return linksArray |
| 114 | +} |
| 115 | + |
| 116 | +func getBboxString(bbox *pdoknlv3.BBox) string { |
| 117 | + var sb strings.Builder |
| 118 | + sb.WriteString(bbox.MinX + " " + bbox.MinY + " " + bbox.MaxX + " " + bbox.MaxY) |
| 119 | + return sb.String() |
| 120 | +} |
| 121 | + |
| 122 | +func getCategory(srs *pdoknlv3.SRS) []atom_feed.Category { |
| 123 | + cat := []atom_feed.Category{ |
| 124 | + { |
| 125 | + Term: srs.URI, |
| 126 | + Label: srs.Name, |
| 127 | + }, |
| 128 | + } |
| 129 | + return cat |
| 130 | +} |
| 131 | + |
| 132 | +func getBoundingBoxPolygon(bbox pdoknlv3.BBox) string { |
| 133 | + var sb strings.Builder |
| 134 | + // punt links beneden start van een polygon |
| 135 | + sb.WriteString(bbox.MinX + " " + bbox.MinY + " ") |
| 136 | + // punt links boven start van een polygon |
| 137 | + sb.WriteString(bbox.MinX + " " + bbox.MaxY + " ") |
| 138 | + // punt rechts boven start van een polygon |
| 139 | + sb.WriteString(bbox.MaxX + " " + bbox.MaxY + " ") |
| 140 | + // punt rechts beneden start van een polygon |
| 141 | + sb.WriteString(bbox.MaxX + " " + bbox.MinY + " ") |
| 142 | + // punt links beneden. eninde van een polygon is gelijk aan de start |
| 143 | + sb.WriteString(bbox.MinX + " " + bbox.MinY + " ") |
| 144 | + return sb.String() |
| 145 | +} |
| 146 | + |
| 147 | +func getAuthor(author pdoknlv3.Author) atom_feed.Author { |
| 148 | + return atom_feed.Author{ |
| 149 | + Name: author.Name, |
| 150 | + Email: author.Email, |
| 151 | + } |
| 152 | +} |
| 153 | + |
| 154 | +func getSelfLink(atom pdoknlv3.Atom, language string) atom_feed.Link { |
| 155 | + return atom_feed.Link{ |
| 156 | + Rel: "self", |
| 157 | + Href: atom.Spec.Service.BaseURL + "/index.xml", |
| 158 | + Title: strings.Replace(atom.Spec.Service.Title, "\"", "\\\"", -1), |
| 159 | + Type: "application/atom+xml", |
| 160 | + Hreflang: &language, |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +func replaceMustachTemplate(hrefTemplate string, identifier string) (string, error) { |
| 165 | + templateVariable := map[string]string{"identifier": identifier} |
| 166 | + return mustache.Render(hrefTemplate, templateVariable) |
| 167 | +} |
| 168 | + |
| 169 | +func getCSWDescribedbyLink(atom pdoknlv3.Atom, language string, ownerInfo v1.OwnerInfo) (atom_feed.Link, error) { |
| 170 | + for _, template := range atom.Spec.Service.ServiceMetadataLinks.Templates { |
| 171 | + if template == "csw" { |
| 172 | + href, err := replaceMustachTemplate(ownerInfo.Spec.MetadataUrls.CSV.HrefTemplate, atom.Spec.Service.ServiceMetadataLinks.MetadataIdentifier) |
| 173 | + if err != nil { |
| 174 | + return atom_feed.Link{}, err |
| 175 | + } |
| 176 | + return atom_feed.Link{ |
| 177 | + Rel: "describedby", |
| 178 | + Href: href, |
| 179 | + Type: "application/xml", |
| 180 | + Hreflang: &language, |
| 181 | + }, nil |
| 182 | + } |
| 183 | + } |
| 184 | + return atom_feed.Link{}, fmt.Errorf("OwnerInfo heeft geen CSW template") |
| 185 | +} |
| 186 | + |
| 187 | +func getSearchLink(atom pdoknlv3.Atom, language string, ownerInfo v1.OwnerInfo) (atom_feed.Link, error) { |
| 188 | + for _, template := range atom.Spec.Service.ServiceMetadataLinks.Templates { |
| 189 | + if template == "opensearch" { |
| 190 | + href, err := replaceMustachTemplate(ownerInfo.Spec.MetadataUrls.OpenSearch.HrefTemplate, atom.Spec.Service.ServiceMetadataLinks.MetadataIdentifier) |
| 191 | + if err != nil { |
| 192 | + return atom_feed.Link{}, err |
| 193 | + } |
| 194 | + |
| 195 | + return atom_feed.Link{ |
| 196 | + Rel: "search", |
| 197 | + Href: href, |
| 198 | + Type: "application/xml", |
| 199 | + Hreflang: &language, |
| 200 | + }, nil |
| 201 | + } |
| 202 | + } |
| 203 | + return atom_feed.Link{}, fmt.Errorf("OwnerInfo heeft geen opensearch template") |
| 204 | +} |
| 205 | + |
| 206 | +func getHTMLRelatedLink(atom pdoknlv3.Atom, language string, ownerInfo v1.OwnerInfo) (atom_feed.Link, error) { |
| 207 | + for _, template := range atom.Spec.Service.ServiceMetadataLinks.Templates { |
| 208 | + if template == "html" { |
| 209 | + href, err := replaceMustachTemplate(ownerInfo.Spec.MetadataUrls.HTML.HrefTemplate, atom.Spec.Service.ServiceMetadataLinks.MetadataIdentifier) |
| 210 | + if err != nil { |
| 211 | + return atom_feed.Link{}, err |
| 212 | + } |
| 213 | + return atom_feed.Link{ |
| 214 | + Rel: "related", |
| 215 | + Href: href, |
| 216 | + Type: "text/html", |
| 217 | + Hreflang: &language, |
| 218 | + }, nil |
| 219 | + } |
| 220 | + } |
| 221 | + return atom_feed.Link{}, fmt.Errorf("OwnerInfo heeft geen html template") |
| 222 | +} |
0 commit comments