const UserError = require('vn-loopback/util/user-error'); const axios = require('axios'); module.exports = Self => { Self.remoteMethod('optimize', { description: 'Return optimized coords', accessType: 'READ', accepts: [{ arg: 'addressIds', type: 'array', required: true }, { arg: 'firstAddressId', type: 'number', required: false }], returns: { type: 'object', root: true }, http: { path: `/optimize`, verb: 'GET' } }); Self.optimize = async(addressIds, firstAddressId, lastAddressId) => { const models = Self.app.models; try { const osrmConfig = await models.OsrmConfig.findOne(); if (!osrmConfig) throw new UserError(`OSRM service is not configured`); let coords = []; if (firstAddressId) { const address = await models.Address.findById(firstAddressId); if (address.latitude && address.longitude) { coords.push({ addressId: address.id, latitude: address.latitude.toFixed(6), longitude: address.longitude.toFixed(6) }); } } for (const addressId of addressIds) { const address = await models.Address.findById(addressId); if (address.latitude && address.longitude) { coords.push({ addressId, latitude: address.latitude.toFixed(6), longitude: address.longitude.toFixed(6) }); } } if (lastAddressId) { const firstAddress = await models.Address.findById(lastAddressId); if (firstAddress.latitude && firstAddress.longitude) { coords.push({ addressId: firstAddress.id, latitude: firstAddress.latitude.toFixed(6), longitude: firstAddress.longitude.toFixed(6) }); } } if (!coords.length) throw new UserError('No address has coordinates'); const concatCoords = coords .map(coord => `${coord.longitude},${coord.latitude}`) .join(';'); const response = await axios.post(` ${osrmConfig.url}/trip/v1/driving/${concatCoords}?source=first&destination=last&roundtrip=true `); const tolerance = osrmConfig.tolerance; for (const waypoint of response.data.waypoints) { const longitude = waypoint.location[0]; const latitude = waypoint.location[1]; const matchedAddress = coords.find(coord => coord.position === undefined && Math.abs(coord.latitude - latitude) <= tolerance && Math.abs(coord.longitude - longitude) <= tolerance ); if (matchedAddress) matchedAddress.position = waypoint.waypoint_index; } coords.sort((a, b) => { const posA = a.position !== undefined ? a.position : Infinity; const posB = b.position !== undefined ? b.position : Infinity; return posA - posB; }); const coordsString = coords .map(item => `point=${item.latitude},${item.longitude}`) .join('&'); return { coords, view: `https://graphhopper.com/maps/?${coordsString}&profile=small_truck` }; } catch (err) { switch (err.response?.data?.code) { case 'NoTrips': throw new UserError('No trips found because input coordinates are not connected'); case 'NotImplemented': throw new UserError('This request is not supported'); case 'InvalidOptions': throw new UserError('Invalid options or too many coordinates'); default: throw err; } } }; };