1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | 1x 1x 21x 17x 17x 1x 63x 16x 1x 15x 15x 15x 15x 15x 1x 22x 17x 60x 60x 60x 15x 15x 2x 40x 40x 6x 42x 42x 8x | /** * @module server/api/validators/geojson */ const _ = require('lodash'); /** * Returns a ValDSL validator that checks whether a value is a well-formatted bounding box string. * * A bounding box string is composed of 4 comma-separated numbers: * * * The first 2 numbers are the coordinates (longitude & latitude) of the bounding box's south-west corner. * * The last 2 numbers are the coordinates (longitude & latitude) of the bounding box's north-east corner. * * @returns {function} A validator function. */ exports.bboxString = function() { return async function(ctx) { // Make sure the value is a string. const bbox = ctx.get('value'); if (!_.isString(bbox)) { return ctx.addError({ validator: 'bboxString', cause: 'wrongType', message: 'must be a string' }) } // Make sure it has 4 values. const coordinates = bbox.split(',').map(value => parseFloat(value)); if (coordinates.length != 4) { return ctx.addError({ validator: 'bboxString', cause: 'wrongLength', actualLength: coordinates.length, message: `must have 4 comma-separated coordinates; got ${coordinates.length}` }); } // Make sure the 4 values are valid longitudes and latitudes. await Promise.all([ validateBboxStringCoordinate(ctx, coordinates, 0, coordinateCtx => validateLongitude(coordinateCtx)), validateBboxStringCoordinate(ctx, coordinates, 1, coordinateCtx => validateLatitude(coordinateCtx)), validateBboxStringCoordinate(ctx, coordinates, 2, coordinateCtx => validateLongitude(coordinateCtx)), validateBboxStringCoordinate(ctx, coordinates, 3, coordinateCtx => validateLatitude(coordinateCtx)) ]); }; }; /** * Returns a ValDSL validator that checks whether a value is a GeoJSON object of type Point. * * const { point: validateGeoJsonPoint } = require('../validators/geojson'); * * this.validate( * this.json('geometry'), * validateGeoJsonPoint() * ) * * @returns {function} A validator function. */ exports.point = function() { return function(ctx) { return ctx.series( ctx.type('object'), ctx.properties('type', 'coordinates'), ctx.parallel( ctx.validate( ctx.json('/type'), ctx.required(), ctx.type('string'), ctx.equals('Point') ), ctx.validate( ctx.json('/coordinates'), ctx.required(), ctx.type('array'), validateCoordinates, ctx.parallel( ctx.validate( ctx.json('/0'), ctx.type('number'), validateLongitude ), ctx.validate( ctx.json('/1'), ctx.type('number'), validateLatitude ) ) ) ) ); }; } function validateBboxStringCoordinate(ctx, coordinates, i, callback) { return ctx.validate(coordinateCtx => { // Make the error indicate the index of the invalid coordinate // within the bbox string, e.g. "bbox[1]". coordinateCtx.set({ location: `${coordinateCtx.get('location')}[${i}]`, value: coordinates[i] }); return callback(coordinateCtx); }); } function validateCoordinates(ctx) { const coordinates = ctx.get('value'); if (!_.isArray(coordinates) || coordinates.length != 2) { ctx.addError({ validator: 'coordinates', message: 'must be an array of 2 numbers (longitude & latitude)' }); } } function validateLongitude(ctx) { const longitude = ctx.get('value'); if (!_.isFinite(longitude) || longitude < -180 || longitude > 180) { ctx.addError({ validator: 'longitude', message: 'must be a number between -180 and 180' }); } } function validateLatitude(ctx) { const latitude = ctx.get('value'); if (!_.isFinite(latitude) || latitude < -90 || latitude > 90) { ctx.addError({ validator: 'latitude', message: 'must be a number between -90 and 90' }); } } |