@defra/forms-engine-plugin 5.0.0-alpha.25 → 5.0.0-alpha.26

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.
@@ -3,7 +3,8 @@ import { booleanWithin } from '@turf/boolean-within';
3
3
  import { point } from '@turf/helpers';
4
4
  import OsGridRef from 'geodesy/osgridref.js';
5
5
  import JoiBase from 'joi';
6
- import { countries } from "../../../map/routes/index.js";
6
+ import { countriesValidate } from "../../../map/routes/index.js";
7
+ import { CountryMap } from "../../../map/types.js";
7
8
  const Joi = JoiBase.extend({
8
9
  type: 'array',
9
10
  base: JoiBase.array(),
@@ -116,7 +117,7 @@ export function getGeospatialSchema(def) {
116
117
  return applySchemaConstraints(Joi.array().items(featureSchema).unique('id'), def);
117
118
  }
118
119
  const validateCountryBounds = (value, helpers) => {
119
- const countryFeature = countries.features.find(feature => feature.id === country);
120
+ const countryFeature = countriesValidate.features.find(feature => feature.id === CountryMap[country]);
120
121
  if (!countryFeature) {
121
122
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
122
123
  return value;
@@ -135,7 +136,7 @@ export function getGeospatialSchema(def) {
135
136
  }
136
137
  export function getEastingNorthingCountryValidator(field, country) {
137
138
  const validateCountryBounds = (value, helpers) => {
138
- const countryFeature = countries.features.find(feature => feature.id === country);
139
+ const countryFeature = countriesValidate.features.find(feature => feature.id === CountryMap[country]);
139
140
  if (!countryFeature) {
140
141
  // eslint-disable-next-line @typescript-eslint/no-unsafe-return
141
142
  return value;
@@ -1 +1 @@
1
- {"version":3,"file":"geospatial.js","names":["Bourne","booleanWithin","point","OsGridRef","JoiBase","countries","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","determineLimits","def","isOptional","schema","constraints","limits","length","max","applySchemaConstraints","options","optional","getGeospatialSchema","country","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom","getEastingNorthingCountryValidator","field","eastingNorthing","getFormValueFromState","gridRef","easting","northing","latLong","toLatLon","coord","longitude","latitude","within","path","name"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport { point } from '@turf/helpers'\nimport OsGridRef from 'geodesy/osgridref.js'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport { type EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nimport { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countries } from '~/src/server/plugins/map/routes/index.js'\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nexport function determineLimits(\n def: GeospatialFieldComponent,\n isOptional: boolean\n) {\n const { schema: constraints } = def\n const limits: { min?: number; max?: number; length?: number } = {}\n if (typeof constraints?.length === 'number') {\n limits.length = constraints.length\n } else {\n if (typeof constraints?.min === 'number') {\n limits.min = constraints.min\n } else if (!isOptional) {\n limits.min = 1\n }\n\n limits.max = typeof constraints?.max === 'number' ? constraints.max : 50\n }\n return limits\n}\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options } = def\n const isOptional = options.required === false\n\n const limits = determineLimits(def, isOptional)\n\n if (limits.length !== undefined) {\n schema = schema.length(limits.length)\n }\n if (limits.min !== undefined) {\n schema = schema.min(limits.min)\n }\n if (limits.max !== undefined) {\n schema = schema.max(limits.max)\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n\nexport function getEastingNorthingCountryValidator(\n field: EastingNorthingField,\n country: GeospatialFieldOptionsCountry\n) {\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countries.features.find(\n (feature) => feature.id === country\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const eastingNorthing = field.getFormValueFromState(\n value as EastingNorthingState\n )\n\n if (!eastingNorthing) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const gridRef = new OsGridRef(\n eastingNorthing.easting,\n eastingNorthing.northing\n )\n const latLong = gridRef.toLatLon()\n const coord = point([latLong.longitude, latLong.latitude])\n const within = booleanWithin(coord, countryFeature)\n\n if (!within) {\n return helpers.error(\n 'any.custom',\n {\n country\n },\n { path: [field.name] }\n )\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return validateCountryBounds\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"geospatial.js","names":["Bourne","booleanWithin","point","OsGridRef","JoiBase","countriesValidate","CountryMap","Joi","extend","type","base","array","messages","coerce","from","method","value","helpers","trimmed","trim","undefined","parse","result","errors","error","coordinatesSchema","items","number","required","featurePropertiesSchema","object","keys","description","string","coordinateGridReference","centroidGridReference","featureGeometrySchema","valid","coordinates","when","switch","is","then","min","featureSchema","id","properties","geometry","determineLimits","def","isOptional","schema","constraints","limits","length","max","applySchemaConstraints","options","optional","getGeospatialSchema","country","countries","at","unique","validateCountryBounds","countryFeature","features","find","feature","custom","getEastingNorthingCountryValidator","field","eastingNorthing","getFormValueFromState","gridRef","easting","northing","latLong","toLatLon","coord","longitude","latitude","within","path","name"],"sources":["../../../../../../src/server/plugins/engine/components/helpers/geospatial.ts"],"sourcesContent":["import {\n type GeospatialFieldComponent,\n type GeospatialFieldOptionsCountry\n} from '@defra/forms-model'\nimport Bourne from '@hapi/bourne'\nimport { booleanWithin } from '@turf/boolean-within'\nimport { point } from '@turf/helpers'\nimport OsGridRef from 'geodesy/osgridref.js'\nimport JoiBase, { type CustomValidator } from 'joi'\n\nimport { type EastingNorthingField } from '~/src/server/plugins/engine/components/EastingNorthingField.js'\nimport { type EastingNorthingState } from '~/src/server/plugins/engine/components/types.js'\nimport {\n type Coordinates,\n type Feature,\n type FeatureProperties,\n type Geometry\n} from '~/src/server/plugins/engine/types.js'\nimport { countriesValidate } from '~/src/server/plugins/map/routes/index.js'\nimport { CountryMap } from '~/src/server/plugins/map/types.js'\n\nconst Joi = JoiBase.extend({\n type: 'array',\n base: JoiBase.array(),\n messages: {\n 'object.invalidjson': '{{#label}} must be a valid json array string'\n },\n coerce: {\n from: 'string',\n method(value, helpers) {\n if (typeof value === 'string') {\n const trimmed = value.trim()\n if (trimmed === '' || trimmed === '[]') {\n return {\n value: undefined\n }\n }\n\n try {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n return { value: Bourne.parse(value) }\n } catch {\n const result = {\n value,\n errors: [helpers.error('object.invalidjson')]\n }\n\n return result\n }\n } else {\n return {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\n value\n }\n }\n }\n }\n}) as JoiBase.Root\n\nconst coordinatesSchema = Joi.array<Coordinates[]>()\n .items(Joi.number().required(), Joi.number().required())\n .required()\n\nconst featurePropertiesSchema = Joi.object<FeatureProperties>()\n .keys({\n description: Joi.string().required(),\n coordinateGridReference: Joi.string().required(),\n centroidGridReference: Joi.string().required()\n })\n .required()\n\nconst featureGeometrySchema = Joi.object<Geometry>().keys({\n type: Joi.string().valid('Point', 'LineString', 'Polygon').required(),\n coordinates: Joi.array()\n .when('type', {\n switch: [\n { is: 'Point', then: coordinatesSchema },\n {\n is: 'LineString',\n then: Joi.array().items(coordinatesSchema).min(2)\n },\n {\n is: 'Polygon',\n then: Joi.array().items(Joi.array().items(coordinatesSchema).min(3))\n }\n ]\n })\n .required()\n})\n\nconst featureSchema = Joi.object<Feature>().keys({\n id: Joi.string().required(),\n type: Joi.string().valid('Feature').required(),\n properties: featurePropertiesSchema,\n geometry: featureGeometrySchema\n})\n\nexport function determineLimits(\n def: GeospatialFieldComponent,\n isOptional: boolean\n) {\n const { schema: constraints } = def\n const limits: { min?: number; max?: number; length?: number } = {}\n if (typeof constraints?.length === 'number') {\n limits.length = constraints.length\n } else {\n if (typeof constraints?.min === 'number') {\n limits.min = constraints.min\n } else if (!isOptional) {\n limits.min = 1\n }\n\n limits.max = typeof constraints?.max === 'number' ? constraints.max : 50\n }\n return limits\n}\n\nfunction applySchemaConstraints(\n schema: JoiBase.ArraySchema<Feature[]>,\n def: GeospatialFieldComponent\n) {\n const { options } = def\n const isOptional = options.required === false\n\n const limits = determineLimits(def, isOptional)\n\n if (limits.length !== undefined) {\n schema = schema.length(limits.length)\n }\n if (limits.min !== undefined) {\n schema = schema.min(limits.min)\n }\n if (limits.max !== undefined) {\n schema = schema.max(limits.max)\n }\n\n if (isOptional) {\n schema = schema.optional()\n } else {\n schema = schema.required()\n }\n\n return schema\n}\n\nexport function getGeospatialSchema(\n def: GeospatialFieldComponent\n): JoiBase.ArraySchema<Feature[]> {\n const { options = {} } = def\n const country: GeospatialFieldOptionsCountry | undefined =\n options.countries?.at(0)\n\n if (!country) {\n return applySchemaConstraints(\n Joi.array<Feature[]>().items(featureSchema).unique('id'),\n def\n )\n }\n\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countriesValidate.features.find(\n (feature) => feature.id === CountryMap[country]\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const result = booleanWithin(value as Geometry | Feature, countryFeature)\n\n if (!result) {\n return helpers.error('any.custom', {\n country\n })\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return applySchemaConstraints(\n Joi.array<Feature[]>()\n .items(featureSchema.custom(validateCountryBounds))\n .unique('id'),\n def\n )\n}\n\nexport function getEastingNorthingCountryValidator(\n field: EastingNorthingField,\n country: GeospatialFieldOptionsCountry\n) {\n const validateCountryBounds: CustomValidator = (value, helpers) => {\n const countryFeature = countriesValidate.features.find(\n (feature) => feature.id === CountryMap[country]\n )\n\n if (!countryFeature) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const eastingNorthing = field.getFormValueFromState(\n value as EastingNorthingState\n )\n\n if (!eastingNorthing) {\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n const gridRef = new OsGridRef(\n eastingNorthing.easting,\n eastingNorthing.northing\n )\n const latLong = gridRef.toLatLon()\n const coord = point([latLong.longitude, latLong.latitude])\n const within = booleanWithin(coord, countryFeature)\n\n if (!within) {\n return helpers.error(\n 'any.custom',\n {\n country\n },\n { path: [field.name] }\n )\n }\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-return\n return value\n }\n\n return validateCountryBounds\n}\n"],"mappings":"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","ignoreList":[]}
@@ -7,6 +7,10 @@ export function getRoutes(options: MapConfiguration): (ServerRoute<MapProxyGetRe
7
7
  * @type {FeatureCollection}
8
8
  */
9
9
  export const countries: FeatureCollection;
10
+ /**
11
+ * @type {FeatureCollection}
12
+ */
13
+ export const countriesValidate: FeatureCollection;
10
14
  export const countrySchema: Joi.StringSchema<string>;
11
15
  import type { MapConfiguration } from '../../../../server/plugins/map/types.js';
12
16
  import type { MapProxyGetRequestRefs } from '../../../../server/plugins/map/types.js';
@@ -5,14 +5,22 @@ import { StatusCodes } from 'http-status-codes';
5
5
  import Joi from 'joi';
6
6
  import { getAccessToken } from "./get-os-token.js";
7
7
  import { find } from "../service.js";
8
+ import { CountryMap } from "../types.js";
8
9
  import { get, request as httpRequest } from "../../../services/httpService.js";
9
- const filePath = resolve(import.meta.dirname, './vts/countries.geojson');
10
+ const countriesDisplayFilePath = resolve(import.meta.dirname, './vts/countries.geojson');
11
+ const countriesValidateFilePath = resolve(import.meta.dirname, './vts/countries-5dp.geojson');
10
12
 
11
13
  /**
12
14
  * @type {FeatureCollection}
13
15
  */
14
16
  // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
15
- export const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'));
17
+ export const countries = JSON.parse(fs.readFileSync(countriesDisplayFilePath, 'utf8'));
18
+
19
+ /**
20
+ * @type {FeatureCollection}
21
+ */
22
+ // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
23
+ export const countriesValidate = JSON.parse(fs.readFileSync(countriesValidateFilePath, 'utf8'));
16
24
  export const countrySchema = Joi.string().valid(...Object.values(GeospatialFieldOptionsCountryEnum));
17
25
 
18
26
  /**
@@ -169,13 +177,13 @@ function getGeospatialCountries() {
169
177
  if (omit) {
170
178
  return {
171
179
  ...countries,
172
- features: countries.features.filter(feature => feature.id !== omit)
180
+ features: countries.features.filter(feature => feature.id !== CountryMap[omit])
173
181
  };
174
182
  }
175
183
  if (only) {
176
184
  return {
177
185
  ...countries,
178
- features: countries.features.filter(feature => feature.id === only)
186
+ features: countries.features.filter(feature => feature.id === CountryMap[only])
179
187
  };
180
188
  }
181
189
  return countries;
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["fs","resolve","GeospatialFieldOptionsCountryEnum","StatusCodes","Joi","getAccessToken","find","get","request","httpRequest","filePath","import","meta","dirname","countries","JSON","parse","readFileSync","countrySchema","string","valid","Object","values","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","getGeospatialCountries","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","token","searchParams","set","proxyResponse","toString","headers","Authorization","buffer","payload","contentType","res","response","type","auth","validate","object","keys","required","optional","z","y","x","params","getBuffer","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","directory","omit","only","features","filter","feature","id"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import fs from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'\nimport { find } from '~/src/server/plugins/map/service.js'\nimport {\n get,\n request as httpRequest\n} from '~/src/server/services/httpService.js'\n\nconst filePath = resolve(import.meta.dirname, './vts/countries.geojson')\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countries = JSON.parse(fs.readFileSync(filePath, 'utf8'))\n\nexport const countrySchema = Joi.string().valid(\n ...Object.values(GeospatialFieldOptionsCountryEnum)\n)\n\n/**\n * Gets the map support routes\n * @param {MapConfiguration} options - ordnance survey names api key\n */\nexport function getRoutes(options) {\n return [\n mapStyleResourceRoutes(),\n mapProxyRoute(options),\n tileProxyRoute(options),\n geocodeProxyRoute(options),\n getGeospatialCountries()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for the VTS map source by forwarding on the request\n * and adding the auth token and SRS (spatial reference system)\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/map-proxy',\n handler: async (request, h) => {\n const { query } = request\n const targetUrl = new URL(decodeURIComponent(query.url))\n const token = await getAccessToken(options)\n\n targetUrl.searchParams.set('srs', '3857')\n\n const proxyResponse = await httpRequest('get', targetUrl.toString(), {\n headers: {\n Authorization: `Bearer ${token}`\n }\n })\n const buffer = proxyResponse.payload\n const contentType = proxyResponse.res.headers['content-type']\n const response = h.response(buffer)\n\n if (contentType) {\n response.type(contentType)\n }\n\n return response\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n url: Joi.string().required()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.uk\n * Used for VTS map tiles forwarding on the request and adding the auth token\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction tileProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/tile/{z}/{y}/{x}.pbf',\n handler: async (request, h) => {\n const { z, y, x } = request.params\n const token = await getAccessToken(options)\n\n const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`\n\n const getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n },\n options: {\n auth: false\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n auth: false,\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}\n */\nfunction getGeospatialCountries() {\n return {\n method: 'GET',\n path: '/api/maps/countries.geojson',\n handler: (request) => {\n const { omit, only } = request.query\n\n if (omit) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id !== omit)\n }\n }\n\n if (only) {\n return {\n ...countries,\n features: countries.features.filter((feature) => feature.id === only)\n }\n }\n\n return countries\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n omit: countrySchema.optional(),\n only: countrySchema.optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { FeatureCollection } from 'geojson'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"index.js","names":["fs","resolve","GeospatialFieldOptionsCountryEnum","StatusCodes","Joi","getAccessToken","find","CountryMap","get","request","httpRequest","countriesDisplayFilePath","import","meta","dirname","countriesValidateFilePath","countries","JSON","parse","readFileSync","countriesValidate","countrySchema","string","valid","Object","values","getRoutes","options","mapStyleResourceRoutes","mapProxyRoute","tileProxyRoute","geocodeProxyRoute","getGeospatialCountries","method","path","handler","h","query","targetUrl","URL","decodeURIComponent","url","token","searchParams","set","proxyResponse","toString","headers","Authorization","buffer","payload","contentType","res","response","type","auth","validate","object","keys","required","optional","z","y","x","params","getBuffer","Accept","json","gunzip","statusCode","OK","valueOf","code","header","_h","data","ordnanceSurveyApiKey","directory","omit","only","features","filter","feature","id"],"sources":["../../../../../src/server/plugins/map/routes/index.js"],"sourcesContent":["import fs from 'node:fs'\nimport { resolve } from 'node:path'\n\nimport { GeospatialFieldOptionsCountryEnum } from '@defra/forms-model'\nimport { StatusCodes } from 'http-status-codes'\nimport Joi from 'joi'\n\nimport { getAccessToken } from '~/src/server/plugins/map/routes/get-os-token.js'\nimport { find } from '~/src/server/plugins/map/service.js'\nimport { CountryMap } from '~/src/server/plugins/map/types.js'\nimport {\n get,\n request as httpRequest\n} from '~/src/server/services/httpService.js'\n\nconst countriesDisplayFilePath = resolve(\n import.meta.dirname,\n './vts/countries.geojson'\n)\nconst countriesValidateFilePath = resolve(\n import.meta.dirname,\n './vts/countries-5dp.geojson'\n)\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countries = JSON.parse(\n fs.readFileSync(countriesDisplayFilePath, 'utf8')\n)\n\n/**\n * @type {FeatureCollection}\n */\n// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment\nexport const countriesValidate = JSON.parse(\n fs.readFileSync(countriesValidateFilePath, 'utf8')\n)\n\nexport const countrySchema = Joi.string().valid(\n ...Object.values(GeospatialFieldOptionsCountryEnum)\n)\n\n/**\n * Gets the map support routes\n * @param {MapConfiguration} options - ordnance survey names api key\n */\nexport function getRoutes(options) {\n return [\n mapStyleResourceRoutes(),\n mapProxyRoute(options),\n tileProxyRoute(options),\n geocodeProxyRoute(options),\n getGeospatialCountries()\n ]\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.com\n * Used for the VTS map source by forwarding on the request\n * and adding the auth token and SRS (spatial reference system)\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/map-proxy',\n handler: async (request, h) => {\n const { query } = request\n const targetUrl = new URL(decodeURIComponent(query.url))\n const token = await getAccessToken(options)\n\n targetUrl.searchParams.set('srs', '3857')\n\n const proxyResponse = await httpRequest('get', targetUrl.toString(), {\n headers: {\n Authorization: `Bearer ${token}`\n }\n })\n const buffer = proxyResponse.payload\n const contentType = proxyResponse.res.headers['content-type']\n const response = h.response(buffer)\n\n if (contentType) {\n response.type(contentType)\n }\n\n return response\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n url: Joi.string().required()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * Proxies ordnance survey requests from the front end to api.os.uk\n * Used for VTS map tiles forwarding on the request and adding the auth token\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction tileProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/tile/{z}/{y}/{x}.pbf',\n handler: async (request, h) => {\n const { z, y, x } = request.params\n const token = await getAccessToken(options)\n\n const url = `https://api.os.uk/maps/vector/v1/vts/tile/${z}/${y}/${x}.pbf?srs=3857`\n\n const getBuffer = /** @type {typeof get<Buffer>} */ (get)\n\n const { payload, res } = await getBuffer(url, {\n headers: {\n Authorization: `Bearer ${token}`,\n Accept: 'application/x-protobuf'\n },\n json: false,\n gunzip: true\n })\n\n if (res.statusCode && res.statusCode !== StatusCodes.OK.valueOf()) {\n return h.response('Tile fetch failed').code(res.statusCode)\n }\n\n return h\n .response(payload)\n .type('application/x-protobuf')\n .header('Cache-Control', 'public, max-age=86400')\n },\n options: {\n auth: false\n }\n }\n}\n\n/**\n * Proxies ordnance survey geocode requests from the front end to api.os.uk\n * Used for the gazzeteer address lookup to find name from query strings like postcode and place names\n * @param {MapConfiguration} options - the map options\n * @returns {ServerRoute<MapGeocodeGetRequestRefs>}\n */\nfunction geocodeProxyRoute(options) {\n return {\n method: 'GET',\n path: '/api/geocode-proxy',\n async handler(request, _h) {\n const { query } = request\n const data = await find(query.query, options.ordnanceSurveyApiKey)\n\n return data\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n query: Joi.string().required()\n })\n .required()\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<MapProxyGetRequestRefs>}\n */\nfunction mapStyleResourceRoutes() {\n return {\n method: 'GET',\n path: '/api/maps/vts/{path*}',\n options: {\n auth: false,\n handler: {\n directory: {\n path: resolve(import.meta.dirname, './vts')\n }\n }\n }\n }\n}\n\n/**\n * Resource routes to return sprites and glyphs\n * @returns {ServerRoute<GeospatialCountriesGetRequestRefs>}\n */\nfunction getGeospatialCountries() {\n return {\n method: 'GET',\n path: '/api/maps/countries.geojson',\n handler: (request) => {\n const { omit, only } = request.query\n\n if (omit) {\n return {\n ...countries,\n features: countries.features.filter(\n (feature) => feature.id !== CountryMap[omit]\n )\n }\n }\n\n if (only) {\n return {\n ...countries,\n features: countries.features.filter(\n (feature) => feature.id === CountryMap[only]\n )\n }\n }\n\n return countries\n },\n options: {\n auth: false,\n validate: {\n query: Joi.object()\n .keys({\n omit: countrySchema.optional(),\n only: countrySchema.optional()\n })\n .optional()\n }\n }\n }\n}\n\n/**\n * @import { ServerRoute } from '@hapi/hapi'\n * @import { FeatureCollection } from 'geojson'\n * @import { MapConfiguration, MapProxyGetRequestRefs, MapGeocodeGetRequestRefs, GeospatialCountriesGetRequestRefs } from '~/src/server/plugins/map/types.js'\n */\n"],"mappings":"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","ignoreList":[]}
@@ -1,5 +1,43 @@
1
+ # VTS
2
+
1
3
  File in this directory are sourced from https://github.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets
2
4
 
3
5
  VTS_OUTDOOR_URL=https://raw.githubusercontent.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets/main/OS_VTS_3857_Outdoor.json
4
6
  VTS_DARK_URL=https://raw.githubusercontent.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets/main/OS_VTS_3857_Dark.json
5
7
  VTS_BLACK_AND_WHITE_URL=https://raw.githubusercontent.com/OrdnanceSurvey/OS-Vector-Tile-API-Stylesheets/main/OS_VTS_3857_Black_and_White.json
8
+
9
+ # Country boundaries
10
+
11
+ The [Office for National Statistics provides full-resolution boundaries](https://www.ons.gov.uk/methodology/geography/geographicalproducts/digitalboundaries) in two coastal forms.
12
+ We use "BFE" a full resolution — extent of the realm and generally extends to Mean Low Water rather than clipping at Mean High Water.
13
+
14
+ The latest version of the data is from 2025 and can be found at the following links:
15
+
16
+ https://geoportal.statistics.gov.uk/datasets/591803c2a3b94804a3d7895cc127d7c0_0/explore?location=50.529309%2C-4.098782%2C7
17
+
18
+ Download from: http://geoportal.statistics.gov.uk/ or https://geoportal.statistics.gov.uk/datasets/ons::countries-december-2025-boundaries-uk-bfe/about
19
+
20
+ The source data is for the 2025 revision is 179MB so needs to be reduced to allow rendering in a Web map and used for validation on the server. The downloaded file is named `boundaries-bfe.geojson`. `org2ogr` is used to produce two files:
21
+
22
+ ## Server-side validation (boundaries-bfe-5dp.geojson)
23
+
24
+ This is the full file but with coordinate precision reduced from 14 to 5 decimal places, resulting in a smaller file of 83.7MB.
25
+ This data is then used server-side to validate points, shapes and lines that include country restrictions.
26
+
27
+ `ogr2ogr --config OGR_GEOJSON_MAX_OBJ_SIZE 0 -f GeoJSON boundaries-bfe-5dp.geojson boundaries-bfe.geojson -lco COORDINATE_PRECISION=5`
28
+
29
+ ## Client-side display on map (boundaries-bfe-5dp.geojson)
30
+
31
+ To produce a file suitable for use in the map in the browser we use `ogr2ogr`'s `simplify` option.
32
+
33
+ `-simplify 0.001` uses the units of the input CRS. In EPSG:4326, that's degrees, not metres so a tolerance of 0.001° is roughly 111 metres north–south, which is quite a substantial simplification for coastal boundaries. For 0.001 to mean approximately one metre, we have to reproject to a suitable projected CRS (BNG, EPSG:27700) before simplifying:
34
+
35
+ The following command produces `boundaries-bng.geojson`, the source file reprojected to BNG.
36
+
37
+ `ogr2ogr --config OGR_GEOJSON_MAX_OBJ_SIZE 0 -f GeoJSON boundaries-bng.geojson boundaries-bfe.geojson -t_srs EPSG:27700`
38
+
39
+ We then use this to create a simplified dataset, `countries.geojson`, reprojected back in ESPG:4326:
40
+
41
+ `ogr2ogr --config OGR_GEOJSON_MAX_OBJ_SIZE 0 -f GeoJSON countries.geojson boundaries-bng.json -simplify 100 -t_srs EPSG:4326 -lco COORDINATE_PRECISION=5`
42
+
43
+ The result is a file of 940kB that is suitable to use in the web map.
@@ -0,0 +1,4 @@
1
+ [diffend] Oversized file quarantined before diffing.
2
+ name: package/.server/server/plugins/map/routes/vts/countries-5dp.geojson
3
+ size: 83733031 bytes
4
+ sha256: 032725bc5cedcee3aeba36dc8bc6aa987343635c199f5fe723dc8b0478ae5954