@basemaps/lambda-tiler 6.29.0 → 6.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (259) hide show
  1. package/CHANGELOG.md +63 -0
  2. package/build/__tests__/config.data.d.ts +11 -0
  3. package/build/__tests__/config.data.d.ts.map +1 -0
  4. package/build/__tests__/config.data.js +112 -0
  5. package/build/__tests__/config.data.js.map +1 -0
  6. package/build/__tests__/index.test.js +5 -14
  7. package/build/__tests__/index.test.js.map +1 -0
  8. package/build/__tests__/tile.style.json.test.js +1 -0
  9. package/build/__tests__/tile.style.json.test.js.map +1 -0
  10. package/build/__tests__/wmts.capability.test.d.ts +1 -1
  11. package/build/__tests__/wmts.capability.test.d.ts.map +1 -1
  12. package/build/__tests__/wmts.capability.test.js +286 -125
  13. package/build/__tests__/wmts.capability.test.js.map +1 -0
  14. package/build/__tests__/xyz.util.d.ts +7 -11
  15. package/build/__tests__/xyz.util.d.ts.map +1 -1
  16. package/build/__tests__/xyz.util.js +14 -42
  17. package/build/__tests__/xyz.util.js.map +1 -0
  18. package/build/index.d.ts +0 -2
  19. package/build/index.d.ts.map +1 -1
  20. package/build/index.js +68 -41
  21. package/build/index.js.map +1 -0
  22. package/build/routes/__tests__/attribution.test.js +351 -399
  23. package/build/routes/__tests__/attribution.test.js.map +1 -0
  24. package/build/routes/__tests__/fonts.test.js +17 -3
  25. package/build/routes/__tests__/fonts.test.js.map +1 -0
  26. package/build/routes/__tests__/health.test.js +17 -13
  27. package/build/routes/__tests__/health.test.js.map +1 -0
  28. package/build/routes/__tests__/imagery.test.js +1 -0
  29. package/build/routes/__tests__/imagery.test.js.map +1 -0
  30. package/build/routes/__tests__/memory.fs.js +1 -0
  31. package/build/routes/__tests__/memory.fs.js.map +1 -0
  32. package/build/routes/__tests__/sprites.test.js +7 -0
  33. package/build/routes/__tests__/sprites.test.js.map +1 -0
  34. package/build/routes/__tests__/tile.json.test.d.ts +2 -0
  35. package/build/routes/__tests__/tile.json.test.d.ts.map +1 -0
  36. package/build/routes/__tests__/tile.json.test.js +124 -0
  37. package/build/routes/__tests__/tile.json.test.js.map +1 -0
  38. package/build/routes/__tests__/tile.style.json.test.d.ts +2 -0
  39. package/build/routes/__tests__/tile.style.json.test.d.ts.map +1 -0
  40. package/build/routes/__tests__/tile.style.json.test.js +95 -0
  41. package/build/routes/__tests__/tile.style.json.test.js.map +1 -0
  42. package/build/routes/__tests__/wmts.test.js +37 -27
  43. package/build/routes/__tests__/wmts.test.js.map +1 -0
  44. package/build/{__tests__ → routes/__tests__}/xyz.test.d.ts +0 -0
  45. package/build/routes/__tests__/xyz.test.d.ts.map +1 -0
  46. package/build/routes/__tests__/xyz.test.js +99 -0
  47. package/build/routes/__tests__/xyz.test.js.map +1 -0
  48. package/build/routes/attribution.d.ts +7 -5
  49. package/build/routes/attribution.d.ts.map +1 -1
  50. package/build/routes/attribution.js +50 -91
  51. package/build/routes/attribution.js.map +1 -0
  52. package/build/routes/fonts.d.ts +1 -1
  53. package/build/routes/fonts.d.ts.map +1 -1
  54. package/build/routes/fonts.js +33 -10
  55. package/build/routes/fonts.js.map +1 -0
  56. package/build/routes/health.d.ts +3 -3
  57. package/build/routes/health.d.ts.map +1 -1
  58. package/build/routes/health.js +16 -13
  59. package/build/routes/health.js.map +1 -0
  60. package/build/routes/imagery.d.ts +8 -1
  61. package/build/routes/imagery.d.ts.map +1 -1
  62. package/build/routes/imagery.js +17 -17
  63. package/build/routes/imagery.js.map +1 -0
  64. package/build/routes/ping.d.ts +3 -0
  65. package/build/routes/ping.d.ts.map +1 -0
  66. package/build/routes/ping.js +7 -0
  67. package/build/routes/ping.js.map +1 -0
  68. package/build/routes/sprites.d.ts.map +1 -1
  69. package/build/routes/sprites.js +22 -22
  70. package/build/routes/sprites.js.map +1 -0
  71. package/build/routes/tile.json.d.ts +7 -1
  72. package/build/routes/tile.json.d.ts.map +1 -1
  73. package/build/routes/tile.json.js +19 -22
  74. package/build/routes/tile.json.js.map +1 -0
  75. package/build/routes/tile.style.json.d.ts +6 -1
  76. package/build/routes/tile.style.json.d.ts.map +1 -1
  77. package/build/routes/tile.style.json.js +11 -13
  78. package/build/routes/tile.style.json.js.map +1 -0
  79. package/build/routes/tile.wmts.d.ts +9 -3
  80. package/build/routes/tile.wmts.d.ts.map +1 -1
  81. package/build/routes/tile.wmts.js +37 -50
  82. package/build/routes/tile.wmts.js.map +1 -0
  83. package/build/routes/tile.xyz.d.ts +14 -4
  84. package/build/routes/tile.xyz.d.ts.map +1 -1
  85. package/build/routes/tile.xyz.js +22 -17
  86. package/build/routes/tile.xyz.js.map +1 -0
  87. package/build/routes/tile.xyz.raster.d.ts +11 -0
  88. package/build/routes/tile.xyz.raster.d.ts.map +1 -0
  89. package/build/routes/tile.xyz.raster.js +90 -0
  90. package/build/routes/tile.xyz.raster.js.map +1 -0
  91. package/build/routes/tile.xyz.vector.d.ts +8 -0
  92. package/build/routes/tile.xyz.vector.d.ts.map +1 -0
  93. package/build/routes/tile.xyz.vector.js +46 -0
  94. package/build/routes/tile.xyz.vector.js.map +1 -0
  95. package/build/routes/version.d.ts +3 -0
  96. package/build/routes/version.d.ts.map +1 -0
  97. package/build/routes/version.js +9 -0
  98. package/build/routes/version.js.map +1 -0
  99. package/build/util/__test__/validate.test.d.ts +2 -0
  100. package/build/util/__test__/validate.test.d.ts.map +1 -0
  101. package/build/util/__test__/validate.test.js +66 -0
  102. package/build/util/__test__/validate.test.js.map +1 -0
  103. package/build/util/cotar.serve.d.ts +20 -0
  104. package/build/util/cotar.serve.d.ts.map +1 -0
  105. package/build/util/cotar.serve.js +41 -0
  106. package/build/util/cotar.serve.js.map +1 -0
  107. package/build/util/etag.d.ts +6 -0
  108. package/build/util/etag.d.ts.map +1 -0
  109. package/build/util/etag.js +20 -0
  110. package/build/util/etag.js.map +1 -0
  111. package/build/util/response.d.ts +4 -0
  112. package/build/util/response.d.ts.map +1 -0
  113. package/build/util/response.js +4 -0
  114. package/build/util/response.js.map +1 -0
  115. package/build/util/source.cache.d.ts +28 -0
  116. package/build/util/source.cache.d.ts.map +1 -0
  117. package/build/util/source.cache.js +53 -0
  118. package/build/util/source.cache.js.map +1 -0
  119. package/build/{source.tracer.d.ts → util/source.tracer.d.ts} +1 -0
  120. package/build/util/source.tracer.d.ts.map +1 -0
  121. package/build/{source.tracer.js → util/source.tracer.js} +4 -0
  122. package/build/util/source.tracer.js.map +1 -0
  123. package/build/util/swapping.lru.d.ts +21 -0
  124. package/build/util/swapping.lru.d.ts.map +1 -0
  125. package/build/util/swapping.lru.js +56 -0
  126. package/build/util/swapping.lru.js.map +1 -0
  127. package/build/util/validate.d.ts +46 -0
  128. package/build/util/validate.d.ts.map +1 -0
  129. package/build/util/validate.js +107 -0
  130. package/build/util/validate.js.map +1 -0
  131. package/build/wmts.capability.d.ts +27 -13
  132. package/build/wmts.capability.d.ts.map +1 -1
  133. package/build/wmts.capability.js +156 -55
  134. package/build/wmts.capability.js.map +1 -0
  135. package/dist/index.js +89 -73
  136. package/dist/node_modules/.package-lock.json +1 -1
  137. package/dist/package-lock.json +2 -2
  138. package/dist/package.json +1 -1
  139. package/package.json +10 -10
  140. package/src/__tests__/config.data.ts +120 -0
  141. package/src/__tests__/index.test.ts +4 -20
  142. package/src/__tests__/wmts.capability.test.ts +312 -139
  143. package/src/__tests__/xyz.util.ts +17 -45
  144. package/src/index.ts +75 -41
  145. package/src/routes/__tests__/attribution.test.ts +356 -403
  146. package/src/routes/__tests__/fonts.test.ts +18 -3
  147. package/src/routes/__tests__/health.test.ts +17 -13
  148. package/src/routes/__tests__/sprites.test.ts +6 -1
  149. package/src/routes/__tests__/tile.json.test.ts +145 -0
  150. package/src/routes/__tests__/tile.style.json.test.ts +105 -0
  151. package/src/routes/__tests__/wmts.test.ts +44 -34
  152. package/src/routes/__tests__/xyz.test.ts +119 -0
  153. package/src/routes/attribution.ts +59 -111
  154. package/src/routes/fonts.ts +32 -10
  155. package/src/routes/health.ts +17 -16
  156. package/src/routes/imagery.ts +18 -15
  157. package/src/routes/ping.ts +8 -0
  158. package/src/routes/sprites.ts +20 -22
  159. package/src/routes/tile.json.ts +24 -19
  160. package/src/routes/tile.style.json.ts +15 -12
  161. package/src/routes/tile.wmts.ts +41 -44
  162. package/src/routes/tile.xyz.raster.ts +106 -0
  163. package/src/routes/tile.xyz.ts +31 -16
  164. package/src/routes/tile.xyz.vector.ts +47 -0
  165. package/src/routes/version.ts +8 -0
  166. package/src/util/__test__/validate.test.ts +74 -0
  167. package/src/util/cotar.serve.ts +46 -0
  168. package/src/util/etag.ts +20 -0
  169. package/src/util/response.ts +4 -0
  170. package/src/util/source.cache.ts +71 -0
  171. package/src/{source.tracer.ts → util/source.tracer.ts} +4 -0
  172. package/src/util/swapping.lru.ts +63 -0
  173. package/src/util/validate.ts +126 -0
  174. package/src/wmts.capability.ts +170 -68
  175. package/tsconfig.tsbuildinfo +1 -1
  176. package/build/__tests__/route.test.d.ts +0 -2
  177. package/build/__tests__/route.test.d.ts.map +0 -1
  178. package/build/__tests__/route.test.js +0 -20
  179. package/build/__tests__/tiff.cache.test.d.ts +0 -2
  180. package/build/__tests__/tiff.cache.test.d.ts.map +0 -1
  181. package/build/__tests__/tiff.cache.test.js +0 -58
  182. package/build/__tests__/tile.cache.key.test.d.ts +0 -2
  183. package/build/__tests__/tile.cache.key.test.d.ts.map +0 -1
  184. package/build/__tests__/tile.cache.key.test.js +0 -48
  185. package/build/__tests__/tile.set.cache.test.d.ts +0 -2
  186. package/build/__tests__/tile.set.cache.test.d.ts.map +0 -1
  187. package/build/__tests__/tile.set.cache.test.js +0 -123
  188. package/build/__tests__/tile.set.test.d.ts +0 -2
  189. package/build/__tests__/tile.set.test.d.ts.map +0 -1
  190. package/build/__tests__/tile.set.test.js +0 -11
  191. package/build/__tests__/xyz.test.d.ts.map +0 -1
  192. package/build/__tests__/xyz.test.js +0 -306
  193. package/build/api.key.d.ts +0 -2
  194. package/build/api.key.d.ts.map +0 -1
  195. package/build/api.key.js +0 -23
  196. package/build/cli/dump.d.ts +0 -2
  197. package/build/cli/dump.d.ts.map +0 -1
  198. package/build/cli/dump.js +0 -47
  199. package/build/cli/tile.set.local.d.ts +0 -12
  200. package/build/cli/tile.set.local.d.ts.map +0 -1
  201. package/build/cli/tile.set.local.js +0 -39
  202. package/build/router.d.ts +0 -15
  203. package/build/router.d.ts.map +0 -1
  204. package/build/router.js +0 -49
  205. package/build/routes/api.d.ts +0 -5
  206. package/build/routes/api.d.ts.map +0 -1
  207. package/build/routes/api.js +0 -16
  208. package/build/routes/esri/rest.d.ts +0 -10
  209. package/build/routes/esri/rest.d.ts.map +0 -1
  210. package/build/routes/esri/rest.js +0 -87
  211. package/build/routes/response.d.ts +0 -4
  212. package/build/routes/response.d.ts.map +0 -1
  213. package/build/routes/response.js +0 -3
  214. package/build/routes/tile.d.ts +0 -3
  215. package/build/routes/tile.d.ts.map +0 -1
  216. package/build/routes/tile.etag.d.ts +0 -11
  217. package/build/routes/tile.etag.d.ts.map +0 -1
  218. package/build/routes/tile.etag.js +0 -29
  219. package/build/routes/tile.js +0 -27
  220. package/build/source.tracer.d.ts.map +0 -1
  221. package/build/tiff.cache.d.ts +0 -17
  222. package/build/tiff.cache.d.ts.map +0 -1
  223. package/build/tiff.cache.js +0 -45
  224. package/build/tile.set.cache.d.ts +0 -21
  225. package/build/tile.set.cache.d.ts.map +0 -1
  226. package/build/tile.set.cache.js +0 -100
  227. package/build/tile.set.d.ts +0 -4
  228. package/build/tile.set.d.ts.map +0 -1
  229. package/build/tile.set.js +0 -1
  230. package/build/tile.set.raster.d.ts +0 -49
  231. package/build/tile.set.raster.d.ts.map +0 -1
  232. package/build/tile.set.raster.js +0 -186
  233. package/build/tile.set.vector.d.ts +0 -25
  234. package/build/tile.set.vector.d.ts.map +0 -1
  235. package/build/tile.set.vector.js +0 -71
  236. package/build/validate.d.ts +0 -16
  237. package/build/validate.d.ts.map +0 -1
  238. package/build/validate.js +0 -31
  239. package/src/__tests__/route.test.ts +0 -24
  240. package/src/__tests__/tiff.cache.test.ts +0 -73
  241. package/src/__tests__/tile.cache.key.test.ts +0 -56
  242. package/src/__tests__/tile.set.cache.test.ts +0 -146
  243. package/src/__tests__/tile.set.test.ts +0 -12
  244. package/src/__tests__/xyz.test.ts +0 -362
  245. package/src/api.key.ts +0 -23
  246. package/src/cli/dump.ts +0 -61
  247. package/src/cli/tile.set.local.ts +0 -51
  248. package/src/router.ts +0 -58
  249. package/src/routes/api.ts +0 -19
  250. package/src/routes/esri/rest.ts +0 -90
  251. package/src/routes/response.ts +0 -4
  252. package/src/routes/tile.etag.ts +0 -36
  253. package/src/routes/tile.ts +0 -23
  254. package/src/tiff.cache.ts +0 -51
  255. package/src/tile.set.cache.ts +0 -111
  256. package/src/tile.set.raster.ts +0 -228
  257. package/src/tile.set.ts +0 -4
  258. package/src/tile.set.vector.ts +0 -79
  259. package/src/validate.ts +0 -32
@@ -1,12 +1,10 @@
1
- import { Bounds, Epsg, GoogleTms, ImageFormat, Nztm2000QuadTms, Nztm2000Tms } from '@basemaps/geo';
2
- import { TileSetName, V, VNodeElement } from '@basemaps/shared';
1
+ import { ConfigImagery } from '@basemaps/config';
2
+ import { GoogleTms, ImageFormat, Nztm2000QuadTms } from '@basemaps/geo';
3
+ import { V, VNodeElement } from '@basemaps/shared';
3
4
  import { roundNumbersInString } from '@basemaps/test/build/rounding.js';
4
- import { createHash } from 'crypto';
5
5
  import o from 'ospec';
6
6
  import { WmtsCapabilities } from '../wmts.capability.js';
7
- import { FakeTileSet, Provider } from './xyz.util.js';
8
-
9
- import 'source-map-support/register.js';
7
+ import { Imagery2193, Imagery3857, Provider, TileSetAerial } from './config.data.js';
10
8
 
11
9
  function tags(node: VNodeElement | null | undefined, tag: string): VNodeElement[] {
12
10
  if (node == null) return [];
@@ -18,16 +16,21 @@ function listTag(node: VNodeElement | null | undefined, tag: string): string[] {
18
16
 
19
17
  o.spec('WmtsCapabilities', () => {
20
18
  const apiKey = 'secret1234';
21
- const tileSet = new FakeTileSet(TileSetName.aerial, GoogleTms);
22
- const tileSetImagery = new FakeTileSet('01E7PJFR9AMQFJ05X9G7FQ3XMW', GoogleTms);
19
+
20
+ const allImagery = new Map();
21
+ allImagery.set(Imagery2193.id, Imagery2193);
22
+ allImagery.set(Imagery3857.id, Imagery3857);
23
23
 
24
24
  o('should output the requested formats', () => {
25
25
  const wmts = new WmtsCapabilities({
26
26
  httpBase: 'https://basemaps.test',
27
27
  provider: Provider,
28
- layers: [tileSet],
28
+ tileMatrix: [GoogleTms],
29
+ tileSet: TileSetAerial,
30
+ imagery: allImagery,
29
31
  apiKey,
30
32
  formats: [ImageFormat.Avif],
33
+ isIndividualLayers: false,
31
34
  }).toVNode();
32
35
 
33
36
  const urls = tags(wmts, 'ResourceURL');
@@ -38,12 +41,106 @@ o.spec('WmtsCapabilities', () => {
38
41
  );
39
42
  });
40
43
 
44
+ o('should be seting encoding to utf-8', () => {
45
+ const xml = new WmtsCapabilities({
46
+ httpBase: 'https://basemaps.test',
47
+ provider: Provider,
48
+ tileMatrix: [GoogleTms],
49
+ tileSet: TileSetAerial,
50
+ imagery: allImagery,
51
+ apiKey,
52
+ formats: [ImageFormat.Avif],
53
+ isIndividualLayers: false,
54
+ }).toXml();
55
+
56
+ o(xml.split('\n')[0]).deepEquals('<?xml version="1.0" encoding="utf-8"?>');
57
+ });
58
+
59
+ o('should support unicorns and rainbows', () => {
60
+ const tileSet = { ...TileSetAerial };
61
+ tileSet.name = '🦄_🌈_2022_0-5m';
62
+ tileSet.title = '🦄 🌈 Imagery (2022)';
63
+ tileSet.description = '🦄 🌈 Description';
64
+ const wmts = new WmtsCapabilities({
65
+ httpBase: 'https://basemaps.test',
66
+ provider: Provider,
67
+ tileMatrix: [GoogleTms],
68
+ tileSet,
69
+ imagery: allImagery,
70
+ apiKey,
71
+ formats: [ImageFormat.Avif],
72
+ isIndividualLayers: false,
73
+ }).toVNode();
74
+
75
+ const urls = tags(wmts, 'ResourceURL');
76
+ o(urls.length).equals(1);
77
+ o(urls[0].attrs.template).equals(
78
+ 'https://basemaps.test/v1/tiles/🦄-🌈-2022-0.5m/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.avif?api=secret1234',
79
+ );
80
+
81
+ const layer = tags(wmts, 'Layer')[0];
82
+
83
+ const title = layer.find('ows:Title')?.toString();
84
+ o(title).equals('<ows:Title>🦄 🌈 Imagery (2022)</ows:Title>');
85
+
86
+ const abstract = layer.find('ows:Abstract')?.toString();
87
+ o(abstract).equals('<ows:Abstract>🦄 🌈 Description</ows:Abstract>');
88
+
89
+ const identifier = layer.find('ows:Identifier')?.toString();
90
+ o(identifier).equals('<ows:Identifier>🦄-🌈-2022-0.5m</ows:Identifier>');
91
+ });
92
+
93
+ o('should sort the sub imagery layers', () => {
94
+ const imageryA = { ...Imagery3857 };
95
+ imageryA.id = 'im_a';
96
+ imageryA.name = 'aaaa';
97
+ imageryA.title = 'aaaa';
98
+
99
+ const imageryB = { ...Imagery3857 };
100
+ imageryB.id = 'im_b';
101
+ imageryB.name = 'bbbb';
102
+ imageryB.title = 'bbbb';
103
+
104
+ const imagery = new Map();
105
+ imagery.set(Imagery3857.id, Imagery3857);
106
+ imagery.set(imageryB.id, imageryB);
107
+ imagery.set(imageryA.id, imageryA);
108
+ const tileSet = {
109
+ ...TileSetAerial,
110
+ layers: [
111
+ ...TileSetAerial.layers,
112
+ { [3857]: imageryB.id, title: imageryB.title, name: imageryB.name },
113
+ { [3857]: imageryA.id, title: imageryA.title, name: imageryA.name },
114
+ ],
115
+ };
116
+
117
+ const wmts = new WmtsCapabilities({
118
+ httpBase: 'https://basemaps.test',
119
+ provider: Provider,
120
+ tileMatrix: [GoogleTms],
121
+ tileSet,
122
+ imagery,
123
+ apiKey,
124
+ isIndividualLayers: true,
125
+ }).toVNode();
126
+
127
+ const layers = tags(wmts, 'Layer').map((c) => c.find('ows:Title')?.textContent);
128
+
129
+ // The base layer "Aerial Imagery" should always be first then all sub layers after
130
+ o(layers).deepEquals(['Aerial Imagery', 'aaaa', 'bbbb', 'Ōtorohanga 0.1m Urban Aerial Photos (2021)']);
131
+ });
132
+
41
133
  o('should build capability xml for tileset and projection', () => {
134
+ const imagery = new Map();
135
+ imagery.set(Imagery3857.id, Imagery3857);
42
136
  const wmts = new WmtsCapabilities({
43
137
  httpBase: 'https://basemaps.test',
44
138
  provider: Provider,
45
- layers: [tileSet],
139
+ tileMatrix: [GoogleTms],
140
+ tileSet: TileSetAerial,
141
+ imagery,
46
142
  apiKey,
143
+ isIndividualLayers: false,
47
144
  });
48
145
 
49
146
  const raw = wmts.toVNode();
@@ -53,10 +150,10 @@ o.spec('WmtsCapabilities', () => {
53
150
  o(serviceId?.find('ows:Title')?.textContent).equals('the title');
54
151
 
55
152
  o(raw.find('TileMatrixSetLink')?.toString()).deepEquals(
56
- V('TileMatrixSetLink', [V('TileMatrixSet', 'EPSG:3857')]).toString(),
153
+ V('TileMatrixSetLink', [V('TileMatrixSet', 'WebMercatorQuad')]).toString(),
57
154
  );
58
155
 
59
- const layer = raw?.find('Contents', 'Layer');
156
+ const layer = raw.find('Contents', 'Layer');
60
157
 
61
158
  o(listTag(layer, 'Format')).deepEquals([
62
159
  V('Format', 'image/jpeg').toString(),
@@ -64,41 +161,47 @@ o.spec('WmtsCapabilities', () => {
64
161
  V('Format', 'image/png').toString(),
65
162
  ]);
66
163
 
67
- o(listTag(layer, 'ows:BoundingBox')).deepEquals([
68
- V('ows:BoundingBox', { crs: Epsg.Google.toUrn() }, [
69
- V('ows:LowerCorner', '-20037508.3427892 -20037508.3427892'),
70
- V('ows:UpperCorner', '20037508.3427892 20037508.3427892'),
71
- ]).toString(),
164
+ o(listTag(layer, 'ows:BoundingBox').map((s) => roundNumbersInString(s, 4))).deepEquals([
165
+ '<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::3857">\n' +
166
+ ' <ows:LowerCorner>19457809.9203 -4609458.5537</ows:LowerCorner>\n' +
167
+ ' <ows:UpperCorner>19509787.0995 -4578883.7424</ows:UpperCorner>\n' +
168
+ '</ows:BoundingBox>',
72
169
  ]);
73
170
 
74
171
  o(listTag(layer, 'ows:WGS84BoundingBox').map((s) => roundNumbersInString(s, 4))).deepEquals([
75
172
  '<ows:WGS84BoundingBox crs="urn:ogc:def:crs:OGC:2:84">\n' +
76
- ' <ows:LowerCorner>-180 -85.0511</ows:LowerCorner>\n' +
77
- ' <ows:UpperCorner>180 85.0511</ows:UpperCorner>\n' +
173
+ ' <ows:LowerCorner>174.7925 -38.2123</ows:LowerCorner>\n' +
174
+ ' <ows:UpperCorner>175.2594 -37.9962</ows:UpperCorner>\n' +
78
175
  '</ows:WGS84BoundingBox>',
79
176
  ]);
80
177
 
81
- o(layer?.find('ows:Abstract')?.textContent).equals('aerial:description');
82
- o(layer?.find('ows:Title')?.textContent).equals('aerial:title');
83
178
  o(layer?.find('ows:Identifier')?.textContent).equals('aerial');
179
+ o(layer?.find('ows:Title')?.textContent).equals('Aerial Imagery');
180
+ o(layer?.find('ows:Abstract')?.textContent).equals('aerial__description');
84
181
 
85
182
  o(layer?.find('Style')?.toString()).equals(
86
183
  V('Style', { isDefault: 'true' }, [V('ows:Title', 'Default Style'), V('ows:Identifier', 'default')]).toString(),
87
184
  );
185
+ });
88
186
 
89
- const urls = tags(layer, 'ResourceURL');
90
- o(urls.length).equals(3);
91
- o(urls[2].toString()).deepEquals(
92
- '<ResourceURL format="image/png" resourceType="tile" ' +
93
- 'template="https://basemaps.test/v1/tiles/aerial/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png?api=secret1234" />',
94
- );
187
+ o('should include output the correct TileMatrix', () => {
188
+ const raw = new WmtsCapabilities({
189
+ httpBase: 'https://basemaps.test',
190
+ provider: Provider,
191
+ tileMatrix: [GoogleTms],
192
+ tileSet: TileSetAerial,
193
+ imagery: allImagery,
194
+ apiKey,
195
+ isIndividualLayers: false,
196
+ }).toVNode();
197
+ const layer = raw.find('Contents', 'Layer');
95
198
 
96
- o(layer?.find('TileMatrixSetLink', 'TileMatrixSet')?.textContent).equals('EPSG:3857');
199
+ o(layer?.find('TileMatrixSetLink', 'TileMatrixSet')?.textContent).equals('WebMercatorQuad');
97
200
 
98
201
  const matrix = tags(raw, 'TileMatrixSet')[1];
99
202
  const matrixId = raw?.find('Contents', 'TileMatrixSet', 'ows:Identifier') ?? null;
100
203
  o(matrix.find('ows:Identifier')).equals(matrixId);
101
- o(matrixId?.textContent).equals('EPSG:3857');
204
+ o(matrixId?.textContent).equals('WebMercatorQuad');
102
205
 
103
206
  o(matrix.find('ows:SupportedCRS')?.textContent).deepEquals('urn:ogc:def:crs:EPSG::3857');
104
207
  o(matrix.find('WellKnownScaleSet')?.textContent).deepEquals(
@@ -123,46 +226,62 @@ o.spec('WmtsCapabilities', () => {
123
226
 
124
227
  compareMatrix(tileMatrices[0], '0', 1, 559082264.028717);
125
228
  compareMatrix(tileMatrices[10], '10', 1024, 545978.773465544);
126
-
127
- const xml = new WmtsCapabilities({
128
- httpBase: 'https://basemaps.test',
129
- provider: Provider,
130
- layers: [tileSet],
131
- apiKey,
132
- }).toXml();
133
-
134
- o(xml).deepEquals('<?xml version="1.0"?>\n' + raw?.toString());
135
-
136
- o(createHash('sha256').update(Buffer.from(xml)).digest('base64')).equals(
137
- 'beknYyMt8v74vK4p84AS3c1OnBSJ+ZE0kan+mMVQS1A=',
138
- );
139
229
  });
140
230
 
141
- o('should allow individual imagery sets', () => {
231
+ o('should output individual imagery sets', () => {
232
+ const imagery = new Map<string, ConfigImagery>();
233
+ imagery.set(Imagery3857.id, Imagery3857);
234
+ imagery.set(Imagery2193.id, Imagery2193);
142
235
  const raw = new WmtsCapabilities({
143
236
  httpBase: 'https://basemaps.test',
144
237
  provider: Provider,
145
- layers: [tileSetImagery],
238
+ tileMatrix: [GoogleTms],
239
+ tileSet: TileSetAerial,
240
+ imagery: imagery,
241
+ formats: [ImageFormat.Png],
242
+ isIndividualLayers: true,
146
243
  }).toVNode();
147
244
 
148
245
  const tms = raw?.find('TileMatrixSet', 'ows:Identifier');
149
246
 
150
- o(tms?.textContent).equals('EPSG:3857');
247
+ o(tms?.textContent).equals('WebMercatorQuad');
151
248
 
152
249
  const urls = Array.from(raw ? raw.tags('ResourceURL') : []);
153
- o(urls.length).equals(3);
154
- o(urls[2].toString()).deepEquals(
250
+ o(urls.length).equals(2);
251
+ o(urls[0].toString()).deepEquals(
252
+ '<ResourceURL format="image/png" resourceType="tile" ' +
253
+ 'template="https://basemaps.test/v1/tiles/aerial/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png" />',
254
+ );
255
+ o(urls[1].toString()).deepEquals(
155
256
  '<ResourceURL format="image/png" resourceType="tile" ' +
156
- 'template="https://basemaps.test/v1/tiles/01E7PJFR9AMQFJ05X9G7FQ3XMW/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png" />',
257
+ 'template="https://basemaps.test/v1/tiles/ōtorohanga-urban-2021-0.1m/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.png" />',
157
258
  );
259
+
260
+ const layers = [...raw.tags('Layer')];
261
+
262
+ o(layers.length).equals(2);
263
+ o(layers[0].find('ows:Title')?.textContent).equals('Aerial Imagery');
264
+
265
+ o(layers[1].find('ows:Title')?.textContent).equals('Ōtorohanga 0.1m Urban Aerial Photos (2021)');
266
+ o(layers[1].find('ows:Identifier')?.textContent).equals('ōtorohanga-urban-2021-0.1m');
267
+ o(layers[1].find('ows:Keywords', 'ows:Keyword')?.textContent).equals('Urban Aerial Photos');
158
268
  });
159
269
 
160
270
  o('should support multiple projections', () => {
161
- const ts = [new FakeTileSet(TileSetName.aerial, Nztm2000Tms), new FakeTileSet(TileSetName.aerial, GoogleTms)];
162
- const xml = new WmtsCapabilities({ httpBase: 'basemaps.test', provider: Provider, layers: ts });
163
- const nodes = xml.toVNode();
271
+ const imagery = new Map<string, ConfigImagery>();
272
+ imagery.set(Imagery3857.id, Imagery3857);
273
+ imagery.set(Imagery2193.id, Imagery2193);
274
+ const raw = new WmtsCapabilities({
275
+ httpBase: 'https://basemaps.test',
276
+ provider: Provider,
277
+ tileMatrix: [GoogleTms, Nztm2000QuadTms],
278
+ tileSet: TileSetAerial,
279
+ imagery: imagery,
280
+ formats: [ImageFormat.Png],
281
+ isIndividualLayers: false,
282
+ }).toVNode();
164
283
 
165
- const layers = tags(nodes, 'Layer');
284
+ const layers = tags(raw, 'Layer');
166
285
  o(layers.length).equals(1);
167
286
  const layer = layers[0];
168
287
 
@@ -171,126 +290,180 @@ o.spec('WmtsCapabilities', () => {
171
290
  'ows:Title',
172
291
  'ows:Abstract',
173
292
  'ows:Identifier',
293
+ 'ows:Keywords',
174
294
  'ows:BoundingBox',
175
295
  'ows:BoundingBox',
176
296
  'ows:WGS84BoundingBox',
177
297
  'Style',
178
298
  'Format',
179
- 'Format',
180
- 'Format',
181
299
  'TileMatrixSetLink',
182
300
  'TileMatrixSetLink',
183
301
  'ResourceURL',
184
- 'ResourceURL',
185
- 'ResourceURL',
186
302
  ]);
187
303
 
304
+ o(layer.find('ows:Title')?.textContent).equals('Aerial Imagery');
305
+ o(layer.find('ows:Keywords')?.toString()).equals(
306
+ '<ows:Keywords>\n <ows:Keyword>Basemap</ows:Keyword>\n</ows:Keywords>',
307
+ );
308
+ o(layer.find('ows:Identifier')?.textContent).equals('aerial');
309
+
188
310
  const sets = tags(layer, 'TileMatrixSet');
189
311
 
190
312
  o(sets.length).equals(2);
191
- o(sets[0].toString()).equals('<TileMatrixSet>EPSG:2193</TileMatrixSet>');
192
- o(sets[1].toString()).equals('<TileMatrixSet>EPSG:3857</TileMatrixSet>');
193
-
194
- const tms = tags(nodes, 'TileMatrixSet').filter((f) => f.find('ows:SupportedCRS') != null);
195
- o(tms.length).equals(2);
196
-
197
- o(tms[0].find('ows:Identifier')?.textContent).equals('EPSG:2193');
198
- o(tms[0].find('ows:SupportedCRS')?.textContent).equals('urn:ogc:def:crs:EPSG::2193');
313
+ o(sets[0].toString()).equals('<TileMatrixSet>WebMercatorQuad</TileMatrixSet>');
314
+ o(sets[1].toString()).equals('<TileMatrixSet>NZTM2000Quad</TileMatrixSet>');
315
+
316
+ const boundingBoxes = tags(layer, 'ows:BoundingBox');
317
+ o(boundingBoxes.length).equals(2);
318
+ o(boundingBoxes[0].attrs.crs).equals('urn:ogc:def:crs:EPSG::3857');
319
+ o(boundingBoxes[0].children.map((c) => c.textContent)).deepEquals([
320
+ '19457809.9203 -4609458.5537',
321
+ '19509787.0995 -4578883.7424',
322
+ ]);
323
+ o(boundingBoxes[1].attrs.crs).equals('urn:ogc:def:crs:EPSG::2193');
324
+ o(boundingBoxes[1].children.map((c) => c.textContent)).deepEquals([
325
+ '5766358.9964 1757351.3045',
326
+ '5793264.8304 1798321.5516',
327
+ ]);
199
328
 
200
- o(tms[1].find('ows:Identifier')?.textContent).equals('EPSG:3857');
201
- o(tms[1].find('ows:SupportedCRS')?.textContent).equals('urn:ogc:def:crs:EPSG::3857');
329
+ const wgs84 = layer.find('ows:WGS84BoundingBox');
330
+ o(wgs84?.attrs.crs).equals('urn:ogc:def:crs:OGC:2:84');
331
+ o(wgs84?.children.map((c) => c.textContent)).deepEquals(['174.79248 -38.212288', '175.259399 -37.996163']);
202
332
  });
203
333
 
204
- o('should support multiple tilesets', () => {
205
- const ts = [
206
- new FakeTileSet(TileSetName.aerial, Nztm2000Tms, 'aerial-title'),
207
- new FakeTileSet('01E7PJFR9AMQFJ05X9G7FQ3XMW', Nztm2000Tms, 'imagery-title'),
208
- ];
209
- const nodes = new WmtsCapabilities({ httpBase: 'basemaps.test', provider: Provider, layers: ts }).toVNode();
210
- const layers = tags(nodes, 'Layer');
211
- o(layers.length).equals(2);
334
+ o('should only output imagery if exists', () => {
335
+ const imagery = new Map<string, ConfigImagery>();
336
+ const raw = new WmtsCapabilities({
337
+ httpBase: 'https://basemaps.test',
338
+ provider: Provider,
339
+ tileMatrix: [Nztm2000QuadTms],
340
+ tileSet: TileSetAerial,
341
+ imagery: imagery,
342
+ formats: [ImageFormat.Png],
343
+ isIndividualLayers: true,
344
+ }).toVNode();
212
345
 
213
- o(layers[0].find('ows:Title')?.textContent).equals('aerial-title');
214
- o(layers[0].find('TileMatrixSet')?.textContent).equals('EPSG:2193');
346
+ const layers = tags(raw, 'Layer');
347
+ o(layers.length).equals(1);
215
348
 
216
- o(layers[1].find('ows:Title')?.textContent).equals('imagery-title');
217
- o(layers[1].find('TileMatrixSet')?.textContent).equals('EPSG:2193');
349
+ imagery.set(Imagery3857.id, Imagery3857);
350
+ const rawB = new WmtsCapabilities({
351
+ httpBase: 'https://basemaps.test',
352
+ provider: Provider,
353
+ tileMatrix: [Nztm2000QuadTms],
354
+ tileSet: TileSetAerial,
355
+ imagery: imagery,
356
+ formats: [ImageFormat.Png],
357
+ isIndividualLayers: true,
358
+ }).toVNode();
359
+
360
+ const layersB = tags(rawB, 'Layer');
361
+ o(layersB.length).equals(1);
218
362
  });
219
363
 
220
- o('should support child tile sets', () => {
221
- const ts = [
222
- new FakeTileSet(
223
- `${TileSetName.aerial}:wairoa_urban_2014-2015_0-10m_RGBA`,
224
- Nztm2000Tms,
225
- 'wairoa_urban_2014-2015_0-10m_RGBA',
226
- ),
227
- new FakeTileSet(
228
- `${TileSetName.aerial}:west-coast_rural_2016-17_0-3m`,
229
- Nztm2000Tms,
230
- 'west-coast_rural_2016-17_0-3m',
231
- ),
364
+ o('should cover the entire WebMercatorBounds', () => {
365
+ const halfSize = GoogleTms.extent.width / 2;
366
+ // Create two fake imagery sets one covers tile z1 x0 y0 another covers tile z1 x1 y1
367
+ // so the entire bounding box should be tile z0 x0 y0 or the full extent
368
+ const imagery = new Map();
369
+ const imageTopLeft = { ...Imagery3857, id: 'im_top_left', name: 'top_left' };
370
+ imageTopLeft.bounds = { x: -halfSize, y: 0, width: halfSize, height: halfSize };
371
+ imagery.set(imageTopLeft.id, imageTopLeft);
372
+
373
+ const imageBottomRight = { ...Imagery3857, id: 'im_bottom_right', name: 'bottom_right' };
374
+ imageBottomRight.bounds = { x: 0, y: -halfSize, width: halfSize, height: halfSize };
375
+ imagery.set(imageBottomRight.id, imageBottomRight);
376
+
377
+ const tileSet = { ...TileSetAerial };
378
+ tileSet.layers = [
379
+ { 3857: imageTopLeft.id, name: 'a_top_left' },
380
+ { 3857: imageBottomRight.id, name: 'b_bottom_right' },
232
381
  ];
233
- const nodes = new WmtsCapabilities({ httpBase: 'basemaps.test', provider: Provider, layers: ts }).toVNode();
234
- const layers = tags(nodes, 'Layer');
235
382
 
236
- o(layers.length).equals(2);
383
+ const raw = new WmtsCapabilities({
384
+ httpBase: 'https://basemaps.test',
385
+ provider: Provider,
386
+ tileMatrix: [GoogleTms],
387
+ tileSet,
388
+ imagery,
389
+ formats: [ImageFormat.Png],
390
+ isIndividualLayers: true,
391
+ }).toVNode();
237
392
 
238
- const boundingBoxes = tags(layers[0], 'ows:BoundingBox');
239
- o(boundingBoxes.length).equals(1);
393
+ const boundingBox = tags(raw, 'ows:WGS84BoundingBox').map((c) =>
394
+ c
395
+ .toString()
396
+ .split('\n')
397
+ .map((c) => c.trim()),
398
+ );
399
+ o(boundingBox[0][1]).deepEquals('<ows:LowerCorner>-180 -85.051129</ows:LowerCorner>');
400
+ o(boundingBox[0][2]).equals('<ows:UpperCorner>180 85.051129</ows:UpperCorner>');
401
+
402
+ o(boundingBox[1][1]).deepEquals('<ows:LowerCorner>-180 0</ows:LowerCorner>');
403
+ o(boundingBox[1][2]).equals('<ows:UpperCorner>0 85.051129</ows:UpperCorner>');
240
404
 
241
- const firstTitle = layers[0].children[0].textContent;
242
- o(firstTitle).equals('wairoa_urban_2014-2015_0-10m_RGBA');
243
- const secondTitle = layers[1].children[0].textContent;
244
- o(secondTitle).equals('west-coast_rural_2016-17_0-3m');
405
+ o(boundingBox[2][1]).deepEquals('<ows:LowerCorner>0 -85.051129</ows:LowerCorner>');
406
+ o(boundingBox[2][2]).equals('<ows:UpperCorner>180 0</ows:UpperCorner>');
245
407
  });
246
408
 
247
- o('should support multiple different projections on different tiles sets', () => {
248
- const ts = [
249
- new FakeTileSet(TileSetName.aerial, Nztm2000Tms, TileSetName.aerial),
250
- new FakeTileSet('01F75X9G7FQ3XMWPJFR9AMQFJ0', Nztm2000Tms, '01F75X9G7FQ3XMWPJFR9AMQFJ0'),
251
- new FakeTileSet('01E7PJFR9AMQFJ05X9G7FQ3XMW', GoogleTms, '01E7PJFR9AMQFJ05X9G7FQ3XMW'),
252
- new FakeTileSet('01E7PJFR9AMQFJ05X9G7FQ3XMW', Nztm2000QuadTms, '01E7PJFR9AMQFJ05X9G7FQ3XMW'),
253
- ];
254
- ts[1].extentOverride = new Bounds(1, 2, 2, 2);
409
+ o('should work when crossing anti meridian', () => {
410
+ const halfSize = GoogleTms.extent.width / 2;
255
411
 
256
- ts[2].tileSet.title = 'aerial_dunedin_urban';
257
- const nodes = new WmtsCapabilities({ httpBase: 'basemaps.test', provider: Provider, layers: ts }).toVNode();
412
+ const imagery = new Map();
413
+ // This image covers z1 x1.5 y1 to z1 x0.5 y1
414
+ // which cross the AM and covers half the width of two tiles
415
+ const imageBottomRight = { ...Imagery3857, id: 'im_bottom_right', name: 'bottom_right' };
416
+ imageBottomRight.bounds = { x: halfSize / 2, y: -halfSize, width: halfSize, height: halfSize };
417
+ imagery.set(imageBottomRight.id, imageBottomRight);
258
418
 
259
- const allMatrixes = tags(nodes, 'TileMatrixSet');
419
+ const tileSet = { ...TileSetAerial };
420
+ tileSet.layers = [{ 3857: imageBottomRight.id, name: 'b_bottom_right' }];
260
421
 
261
- o(allMatrixes[0].children[0].textContent).equals('EPSG:2193');
262
- o(allMatrixes[1].children[0].textContent).equals('EPSG:2193');
263
- o(allMatrixes[2].children[0].textContent).equals('EPSG:3857');
264
- o(allMatrixes[3].children[0].textContent).equals('NZTM2000Quad');
422
+ const raw = new WmtsCapabilities({
423
+ httpBase: 'https://basemaps.test',
424
+ provider: Provider,
425
+ tileMatrix: [GoogleTms],
426
+ tileSet,
427
+ imagery,
428
+ formats: [ImageFormat.Png],
429
+ isIndividualLayers: true,
430
+ }).toVNode();
265
431
 
266
- o(allMatrixes[4].find('ows:Identifier')?.textContent).equals('EPSG:2193');
267
- o(allMatrixes[5].find('ows:Identifier')?.textContent).equals('EPSG:3857');
268
- o(allMatrixes[6].find('ows:Identifier')?.textContent).equals('NZTM2000Quad');
269
- o(allMatrixes.length).equals(7);
432
+ const boundingBox = tags(raw, 'ows:WGS84BoundingBox').map((c) =>
433
+ roundNumbersInString(c.toString(), 4)
434
+ .split('\n')
435
+ .map((c) => c.trim()),
436
+ );
437
+ o(boundingBox[0][1]).deepEquals('<ows:LowerCorner>-180 -85.0511</ows:LowerCorner>');
438
+ o(boundingBox[0][2]).equals('<ows:UpperCorner>180 0</ows:UpperCorner>');
270
439
 
271
- const layers = tags(nodes, 'Layer');
272
- o(layers.length).equals(3);
440
+ o(boundingBox[1][1]).deepEquals('<ows:LowerCorner>-180 -85.0511</ows:LowerCorner>');
441
+ o(boundingBox[1][2]).equals('<ows:UpperCorner>180 0</ows:UpperCorner>');
442
+ });
273
443
 
274
- o(layers[0].find('ows:Title')?.textContent).equals(TileSetName.aerial);
275
- o(layers[0].find('TileMatrixSet')?.textContent).equals('EPSG:2193');
444
+ o('should work with NZTM2000Quad', () => {
445
+ const wmts = new WmtsCapabilities({ tileMatrix: [] } as any);
276
446
 
277
- o(layers[1].find('ows:Identifier')?.textContent).equals('01F75X9G7FQ3XMWPJFR9AMQFJ0');
278
- o(layers[1].find('TileMatrixSet')?.textContent).equals('EPSG:2193');
279
- o(layers[1].find('ows:BoundingBox')?.toString()).equals(
280
- '<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::2193">\n' +
281
- ' <ows:LowerCorner>2 1</ows:LowerCorner>\n' +
282
- ' <ows:UpperCorner>4 3</ows:UpperCorner>\n' +
283
- '</ows:BoundingBox>',
284
- );
447
+ // Full NZTM200Quad coverage
448
+ const bbox = wmts.buildWgs84BoundingBox(Nztm2000QuadTms, []);
449
+ o(bbox.children[0].textContent).equals('-180 -49.929855');
450
+ o(bbox.children[1].textContent).equals('180 2.938603');
285
451
 
286
- o(layers[2].find('ows:Title')?.textContent).equals('aerial_dunedin_urban');
287
- o(layers[2].find('ows:Identifier')?.textContent).equals('01E7PJFR9AMQFJ05X9G7FQ3XMW');
288
- o(layers[2].find('TileMatrixSet')?.textContent).equals('EPSG:3857');
289
- o(layers[2].find('ows:BoundingBox')?.toString()).equals(
290
- '<ows:BoundingBox crs="urn:ogc:def:crs:EPSG::3857">\n' +
291
- ' <ows:LowerCorner>-20037508.3427892 -20037508.3427892</ows:LowerCorner>\n' +
292
- ' <ows:UpperCorner>20037508.3427892 20037508.3427892</ows:UpperCorner>\n' +
293
- '</ows:BoundingBox>',
294
- );
452
+ // Full NZTM200Quad coverage at z1
453
+ const bboxB = wmts.buildWgs84BoundingBox(Nztm2000QuadTms, [
454
+ Nztm2000QuadTms.tileToSourceBounds({ z: 1, x: 0, y: 0 }),
455
+ Nztm2000QuadTms.tileToSourceBounds({ z: 1, x: 1, y: 1 }),
456
+ ]);
457
+ o(bboxB.children[0].textContent).equals('-180 -49.929855');
458
+ o(bboxB.children[1].textContent).equals('180 2.938603');
459
+
460
+ // Full NZTM200Quad coverage at z5
461
+ const tileCount = Nztm2000QuadTms.zooms[5].matrixWidth;
462
+ const bboxC = wmts.buildWgs84BoundingBox(Nztm2000QuadTms, [
463
+ Nztm2000QuadTms.tileToSourceBounds({ z: 5, x: 0, y: 0 }),
464
+ Nztm2000QuadTms.tileToSourceBounds({ z: 5, x: tileCount - 1, y: tileCount - 1 }),
465
+ ]);
466
+ o(bboxC.children[0].textContent).equals('-180 -49.929855');
467
+ o(bboxC.children[1].textContent).equals('180 2.938603');
295
468
  });
296
469
  });