diff --git a/web/src/lib/mongo.ts b/web/src/lib/mongo.ts index a334127..371709b 100644 --- a/web/src/lib/mongo.ts +++ b/web/src/lib/mongo.ts @@ -34,6 +34,17 @@ export async function listTaxa( .toArray() } +export async function listOccurrences( + filter: Record = {}, + _projection: Record = {} +) { + const occurrences = await getCollection('dwc2json', 'ocorrencias') + return await occurrences + .find(filter) + // .project(projection) + .toArray() +} + export async function listTaxaPaginated( filter: Record = {}, page = 0, diff --git a/web/src/lib/queries.ts b/web/src/lib/queries.ts new file mode 100644 index 0000000..3b84104 --- /dev/null +++ b/web/src/lib/queries.ts @@ -0,0 +1,71 @@ +import { listOccurrences } from './mongo.ts' + +type OccurrenceQuery = { + canonicalName?: string + country?: string + stateProvince?: string + county?: string + recordedBy?: string + eventDate?: string + catalogNumber?: string + lat?: number + long?: number + maxDistance?: number +} +export const getOccurrencePlot = async (query: OccurrenceQuery) => { + const { canonicalName, lat, long, maxDistance } = query + const filter: Record = {} + if (canonicalName) filter.canonicalName = new RegExp(canonicalName, 'i') + if (lat && long) { + filter.geoPoint = { + $near: { + $geometry: { + type: 'Point', + coordinates: [long, lat] + }, + $maxDistance: maxDistance ?? 1000 + } + } + } else if (!canonicalName) { + return null + } + + type Occurrence = { + [key: string]: unknown + geoPoint?: { + type: 'Point' + coordinates: [number, number] + } + } + const data = (await listOccurrences(filter)) as Occurrence[] + return data.reduce<{ + type: 'FeatureCollection' + features: Record[] + }>( + (acc, cur) => { + if (cur.geoPoint) { + acc.features.push({ + type: 'Feature', + properties: { + scientificName: cur.scientificName, + locality: cur.locality, + country: cur.country, + stateProvince: cur.stateProvince, + county: cur.county, + recordedBy: cur.recordedBy, + eventDate: cur.eventDate, + catalogNumber: cur.catalogNumber, + decimalLatitude: cur.decimalLatitude, + decimalLongitude: cur.decimalLongitude + }, + geometry: cur.geoPoint + }) + } + return acc + }, + { + type: 'FeatureCollection', + features: [] + } + ) +} diff --git a/web/src/pages/api/plotOccurrences.ts b/web/src/pages/api/plotOccurrences.ts new file mode 100644 index 0000000..327c030 --- /dev/null +++ b/web/src/pages/api/plotOccurrences.ts @@ -0,0 +1,32 @@ +import type { APIContext } from 'astro' + +import { getOccurrencePlot } from '../../lib/queries.ts' + +export async function GET({ request: { url } }: APIContext) { + const searchParams = new URL(url).searchParams + const [canonicalName, lat, long, maxDistance] = [ + searchParams.get('canonicalName') ?? '', + searchParams.get('lat'), + searchParams.get('long'), + searchParams.get('maxDistance') + ] + const data = await getOccurrencePlot({ + canonicalName, + ...(lat && long ? { lat: +lat, long: +long } : {}), + ...(maxDistance ? { maxDistance: +maxDistance } : {}) + }) + if (!data) { + return new Response( + 'Either canonicalName or Latitude and longitude are required', + { + status: 400 + } + ) + } + + return new Response(JSON.stringify(data), { + headers: { + 'Content-Type': 'application/json' + } + }) +}