Skip to content
This repository has been archived by the owner on Aug 1, 2024. It is now read-only.

added isochrones route for fetching #151

Merged
merged 1 commit into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions api/src/actions/isochrones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import APIError from '../helpers/APIError';
import logger from '../config/winston';

import { getCoordinatesFromPointsCollection } from '../database/geodata';

import { getIsochronesByCoordinates } from '../database/isochrones';

export const getIsochronesByPointsSource = async (pointsSource: string, bottomLeft, topRight) => {
if (pointsSource === 'hospitals') {
logger.info('Getting positions of hospitals...');
const points = await getCoordinatesFromPointsCollection('healthSites', bottomLeft, topRight);
logger.info(`Getting isochrones for ${points.length} hospitals...`);
// const pointStrings = points.map((point) => JSON.stringify(point));
const isochrones = await getIsochronesByCoordinates(true);
const isochroneFeatures = [];
isochrones.forEach((item) => {
item.features.forEach((feature) => {
const enhancedFeature = {
type: feature.type,
geometry: feature.geometry,
properties: feature.properties,
id: `${item._id}-${feature.properties.value}`,
};
isochroneFeatures.push(enhancedFeature);
});
});
return isochroneFeatures;
}
throw new APIError(`pointsSource ${pointsSource} is not valid.`, 400, true);
};

export default { getIsochronesByPointsSource };
19 changes: 19 additions & 0 deletions api/src/database/geodata.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import config from '../config/config';
import logger from '../config/winston';
import APIError from '../helpers/APIError';

import mongoDb from './mongoDb/models/geoDataModel';
Expand Down Expand Up @@ -42,4 +43,22 @@ export const getPropertySum = async (tableName, propertyName) => {
}
};

/**
* Returns coordinates of points in database
* @param {string} collectionName
*/
export const getCoordinatesFromPointsCollection = async (
collectionName: string,
bottomLeft: string,
topRight: string,
) => {
if (config.postgresUser && config.postgresPassword && config.postgresDb) {
logger.info('Functionality is not yet implemented in postgresql.');
return;
}
if (config.mongoUri) {
return mongoDb.getCoordinatesFromPointsCollection(collectionName, bottomLeft, topRight);
}
};

export default { getGeoData, getProperty, getUniqueValuesForProperty, getPropertySum };
17 changes: 17 additions & 0 deletions api/src/database/isochrones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import config from '../config/config';
import logger from '../config/winston';
import APIError from '../helpers/APIError';
import mongoDb from './mongoDb/models/isochronesModel';

export const getIsochronesByCoordinates = async (avoidedDisasters: boolean) => {
if (config.postgresUser && config.postgresPassword && config.postgresDb) {
logger.info('Functionality is not yet implemented in postgresql.');
return;
}
if (config.mongoUri) {
return mongoDb.getIsochronesByCoordinates(avoidedDisasters);
}
throw new APIError('No credentials for database', 500, false, undefined);
};

export default { getIsochronesByCoordinates };
40 changes: 40 additions & 0 deletions api/src/database/mongoDb/dbSchemas/isochronesSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import mongoose from 'mongoose';

const FeaturePropertiesSchema = new mongoose.Schema(
{
group_index: Number,
value: Number,
center: [Number, Number],
area: Number,
reachfactor: Number,
total_pop: Number,
},
{ _id: false },
);

const FeatureGeometrySchema = new mongoose.Schema(
{
coordinates: mongoose.Schema.Types.Mixed,
type: String,
},
{ _id: false },
);

const FeaturesSchema = new mongoose.Schema(
{
type: String,
properties: FeaturePropertiesSchema,
geometry: FeatureGeometrySchema,
},
{ _id: false },
);

const IsochronesSchema = new mongoose.Schema({
geoIndexId: { type: [Number, Number], index: '2dsphere' },
geoIndexString: String,
features: [FeaturesSchema],
avoidedDisasters: Boolean,
timestamp: Number,
});

export default mongoose.model('Isochrone', IsochronesSchema);
21 changes: 20 additions & 1 deletion api/src/database/mongoDb/models/geoDataModel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,4 +61,23 @@ const getPropertySum = async (tableName: string, propertyName: string) => {
return data;
};

export default { getGeoData, getProperty, getUniqueValuesForProperty, getPropertySum };
const getCoordinatesFromPointsCollection = async (collectionName: string, bottomLeft: string, topRight: string) => {
let coordinateFilter = {};
if (bottomLeft && topRight) {
coordinateFilter = { bbox: filterCoordinates(coordinateFilter, bottomLeft, topRight) };
}

const { connection } = mongoose;
const { db } = connection;

const data = await db.collection(collectionName).find(coordinateFilter).toArray();
return data.map((item) => item.geometry.coordinates);
};

export default {
getGeoData,
getProperty,
getUniqueValuesForProperty,
getPropertySum,
getCoordinatesFromPointsCollection,
};
10 changes: 10 additions & 0 deletions api/src/database/mongoDb/models/isochronesModel.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import Isochrone from '../dbSchemas/isochronesSchema';

const getIsochronesByCoordinates = async (avoidedDisasters) => {
let isochrones = await Isochrone.find({ avoidedDisasters });
isochrones = isochrones.filter((item) => item);

return isochrones;
};

export default { getIsochronesByCoordinates };
2 changes: 1 addition & 1 deletion api/src/helpers/APIError.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class APIError extends ExtendableError {
* @param {number} status - HTTP status code of error.
* @param {boolean} isPublic - Whether the message should be visible to user or not.
*/
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false, error) {
constructor(message, status = httpStatus.INTERNAL_SERVER_ERROR, isPublic = false, error?) {
super(message, status, isPublic, error);
}
}
Expand Down
31 changes: 31 additions & 0 deletions api/src/openapi/apiSchema.yml
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,37 @@ paths:
responses:
200:
description: Successful resposne
/api/isochrones:
get:
summary: Get isochrones
parameters:
- in: query
name: pointsSource
schema:
type: string
description: one of specified sources for points of interest
- in: query
name: bottomLeft
schema:
type: string
pattern: ^-?[0-9]+.[0-9]+,-?[0-9]+.[0-9]+$
description: Bottom left corner of selected area to fetch features from
required: false
- in: query
name: topRight
schema:
type: string
pattern: ^-?[0-9]+.[0-9]+,-?[0-9]+.[0-9]+$
description: Top right corner of selected area to fetch features from
required: false
- in: query
name: proj
schema:
type: string
description: Projection for coordinates values.
responses:
200:
description: Successful response

/api/staticLayers:
get:
Expand Down
2 changes: 2 additions & 0 deletions api/src/routes/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import attributes from './attributes';
import pointAttributes from './pointAttributes';
import geodata from './geodata';
import uploads from './uploads';
import isochrones from './isochrones';
import authorization from './authorization';
import config from './config';

Expand All @@ -14,6 +15,7 @@ router.use('/staticLayers', dataLayers);
router.use('/attributes', attributes);
router.use('/pointAttributes', pointAttributes);
router.use('/geodata', geodata);
router.use('/isochrones', isochrones);
router.use('/uploads', uploads);
router.use('/authorization', authorization);
router.use('/config', config);
Expand Down
34 changes: 34 additions & 0 deletions api/src/routes/isochrones.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import express from 'express';
import swaggerValidation from '../config/swagger';
import utils from '../helpers/utils';

import { getIsochronesByPointsSource } from '../actions/isochrones';

const router = express.Router();

router.get(
'/',
swaggerValidation.validate,
utils.forwardError(async (req, res) => {
const { bottomLeft, topRight, pointsSource } = req.query;

const itemsArrays = await getIsochronesByPointsSource(pointsSource, bottomLeft, topRight);

const items = itemsArrays; // .length ? itemsArrays.flat() : [];

const featureCollection = {
type: 'FeatureCollection',
name: 'Isochrone for point attribute',
crs: {
type: 'name',
properties: {
name: 'urn:ogc:def:crs:OGC:1.3:CRS84',
},
},
features: items,
};
res.send(featureCollection);
}),
);

export default router;