core/utilities/commonGeometryValidation.js

const { validBoundingBox } = require('../boundingBoxes/validBoundingBox')

/**
 * This tests an object to see if it meets common required criteria between all GeoJSON
 * geometry objects.
 *
 * @memberof Core.Utilities
 * @param {object} geometryObject A GeoJSON geometry object to test for common validity requirements
 * @returns {boolean} True if all common validations passed. Will throw a specific error if it encounters a problem.
 * @throws {Error} Argument must be a GeoJSON Geometry object.
 * @throws {Error} Must have a type property with value 'Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', or 'GeometryCollection'.
 * @throws {Error} GeoJSON Geometry objects are forbidden from having a property 'geometry'.
 * @throws {Error} GeoJSON Geometry objects are forbidden from having a property 'properties'.
 * @throws {Error} GeoJSON Geometry objects are forbidden from having a property 'features'.
 * @throws {Error} GeoJSON Geometry objects (except GeometryCollection) must contain a 'coordinates' property.
 * @throws {Error} Coordinates property must be an array (appropriately structured by geometry type) with valid GeoJSON coordinates.
 */
function commonGeometryValidation(geometryObject) {
    if (typeof geometryObject !== 'object') {
        throw new Error('Argument must be a GeoJSON Geometry object.')
    }

    if (
        // This statement might seem redundant, but it is used for 'anyGeometry' to pass a useful error message back from the start if it cannot figure
        // out what kind of geometry it is supposed to be dealing with.
        !(
            geometryObject.type === 'Point' ||
            geometryObject.type === 'MultiPoint' ||
            geometryObject.type === 'LineString' ||
            geometryObject.type === 'MultiLineString' ||
            geometryObject.type === 'Polygon' ||
            geometryObject.type === 'MultiPolygon' ||
            geometryObject.type === 'GeometryCollection'
        )
    ) {
        throw new Error(
            `Must have a type property with value 'Point', 'MultiPoint', 'LineString', 'MultiLineString', 'Polygon', 'MultiPolygon', or 'GeometryCollection'.`
        )
    }

    if ('geometry' in geometryObject) {
        throw new Error(`GeoJSON Geometry objects are forbidden from having a property 'geometry'.`)
    }

    if ('properties' in geometryObject) {
        throw new Error(
            `GeoJSON Geometry objects are forbidden from having a property 'properties'.`
        )
    }

    if ('features' in geometryObject) {
        throw new Error(`GeoJSON Geometry objects are forbidden from having a property 'features'.`)
    }

    if ('bbox' in geometryObject) {
        validBoundingBox(geometryObject.bbox)
    }

    // Geometry objects are allowed to have empty arrays as coordinates, however validCoordinate may not.
    // If coordinates is an empty array, we're done. Otherwise, check for coordinate validity.
    if (geometryObject.type !== 'GeometryCollection') {
        if (!('coordinates' in geometryObject)) {
            throw new Error(
                `GeoJSON Geometry objects (except GeometryCollection) must contain a 'coordinates' property.`
            )
        }
        if (!Array.isArray(geometryObject.coordinates)) {
            throw new Error(
                'Coordinates property must be an array (appropriately structured by geometry type) with valid GeoJSON coordinates.'
            )
        }
    }
    return true
}

exports.commonGeometryValidation = commonGeometryValidation