/**
* Plugin factory.
*
* **Never** call this function directly!!! It's only-meant to be called by the {@link https://ardhi.github.io/bajo|Bajo framework} during plugin initialization.
*
* @param {string} pkgName - NPM package name
* @returns {class} BajoSpatial
*/
async function factory (pkgName) {
const me = this
/**
* BajoSpatial class definition
*
* @class
*/
class BajoSpatial extends this.app.baseClass.Base {
/**
* Creates an instance of BajoSpatial.
*/
constructor () {
super(pkgName, me.app)
/**
* @property {object} config - Configuration object
*/
this.config = {}
}
/**
* Builds a bounding box query for a given model.
*
* @async
* @method
* @param {object} params - Parameters for building the query.
* @param {string} params.bbox - Bounding box coordinates.
* @param {object} [params.query={}] - Existing query object to merge with.
* @param {object} params.model - {@link https://ardhi.github.io/dobo|Dobo model} to apply the query to.
* @param {object} [params.options={}] - Additional options for query building.
* @param {string} [params.options.bboxLatField='lat'] - Latitude field name in the model.
* @param {string} [params.options.bboxLngField='lng'] - Longitude field name in the model.
* @returns {object} - The modified query object with bounding box conditions.
*/
buildBboxQuery = async (params = {}) => {
let { bbox, query = {}, model, options = {} } = params
const { merge, isEmpty } = this.app.lib._
const props = model.properties.map(item => item.name)
const { bboxLatField = 'lat', bboxLngField = 'lng' } = options
if (props.includes(bboxLatField) && props.includes(bboxLngField)) {
const [minx, miny, maxx, maxy] = await this.parseBbox(bbox)
const q = {}
q[bboxLngField] = { $gte: minx, $lte: maxx }
q[bboxLatField] = { $gte: miny, $lte: maxy }
if (isEmpty(query)) query = q
else {
if (query.$and) {
query.$and.push(q)
} else if (query.$or) {
const old = query.$or
query = { $and: [old, q] }
} else {
merge(query, q)
}
}
}
return query
}
/**
* Gets the bounding box for a given country ID.
* Requires the `bajoCommonDb` and `dobo` plugins to be available. Otherwise, it will return undefined.
*
* @async
* @method
* @param {string} item - Country ID.
* @returns {Promise<string|undefined>} - The bounding box string or undefined if not found.
*/
getCountryBbox = async (item) => {
item = item + ''
if (item.includes(',')) return
if (!this.app.bajoCommonDb || !this.app.dobo) return
const model = this.app.dobo.getModel('CdbCountry')
const country = await model.getRecord(item, { throwNotFound: false })
if (country) return country.bbox
throw this.error('Invalid country bbox \'%s\'', item, { statusCode: 400 })
}
/**
* Parses a bounding box input.
*
* @async
* @method
* @param {string} input - The bounding box input, either a country ID or coordinates.
* @returns {Promise<Array<number>|undefined>} - The parsed bounding box coordinates or undefined if not found.
*/
parseBbox = async (input) => {
const { isSet } = this.app.lib.aneka
if (input.length === 2 && !input.includes(',')) return await this.getCountryBbox(input)
const [minx, miny, maxx, maxy] = input.split(',').map(b => parseFloat(b) || null)
const valid = (isSet(minx) && isSet(miny) && isSet(maxx) && isSet(maxy)) &&
(minx >= -180 && maxx <= 180 && miny >= -90 && maxy <= 90)
// (minx <= maxx && miny <= maxy)
if (valid) return [minx, miny, maxx, maxy]
throw this.error('Invalid bbox \'%s\'', input)
}
}
return BajoSpatial
}
export default factory