Skip to content

Commit

Permalink
Added static map image on search along the route
Browse files Browse the repository at this point in the history
  • Loading branch information
mayurvir committed Apr 19, 2024
1 parent d00c7d2 commit 5110dcb
Show file tree
Hide file tree
Showing 6 changed files with 87 additions and 33 deletions.
6 changes: 3 additions & 3 deletions controllers/Agent.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,9 @@ async function getResponse(req, res) {
const response = await ai.get_response_or_perform_action(messages, raw_yn)

// check for media urls
if(map.routes_image){
const public_url = await actionsService.download_file(map.routes_image);
media_urls.push(public_url);
const media_url = map.media_url || ai.media_url;
if(media_url){
media_urls.push(media_url);
}

// prepare raw body if required
Expand Down
32 changes: 30 additions & 2 deletions services/AI.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import init from '../config/schemas/init.js';
import confirm from '../config/schemas/confirm.js';
import get_text_by_key from '../utils/language.js';
import { EMPTY_SESSION } from '../config/constants.js';
import MapsService from './MapService.js';
const BECKN_ACTIONS = {
search: {
schema : search, call_to_action : "Which one would you like to select?"
Expand Down Expand Up @@ -41,6 +42,7 @@ class AI {
this.bookings = [];
this.actionService = new Actions();
this.session = EMPTY_SESSION;
this.media_url = null;
this.tools = [];
this.attempt = 0; // for API call attempts
}
Expand Down Expand Up @@ -162,10 +164,36 @@ class AI {
response={
status: true,
data: api_response?.data?.responses,
message: api_response.data.responses.length>0 ? BECKN_ACTIONS[action]['call_to_action'] : "No response found for the given action"
message: BECKN_ACTIONS[action]['call_to_action']
}


// in case of search along the route, add a media url for the route image
if(action=='search' && message.intent?.fulfillment?.stops[0].location?.polygon){

const mapService = new MapsService();
logger.warn('Fetching static image for search along the route...')
let markers = [];
response.data.map(res => {
try{
res.message.catalog.providers.map(provider => {
provider.locations.map(location => {
markers.push({
label: location.address,
location: location.gps
})
})
})
}
catch(e){
logger.error(e);
}
})
const static_image_url = mapService.get_static_image_path([this.session.profile.selected_route], markers);
logger.info(`Got static image for search along the route : ${static_image_url}`);
this.media_url = await this.actionService.download_file(static_image_url);

}

// update last action and response
if(api_response?.data?.responses?.length>0){
this.session.profile.last_action = action;
Expand Down
13 changes: 0 additions & 13 deletions services/Actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,19 +34,6 @@ class Actions {
try {
let response = await axios(request)

// optimise search results.
// This code will ensure that for search resylts, only the responses with catalog providers are returned and out of them we only take the first resopnse to further reduce the token size.
// This should be imlemented by different baps based on their requirements.
if(request.data.context && request.data.context.action==='search'){
response.data.responses = response.data.responses.filter(res => res.message && res.message.catalog && res.message.catalog.providers && res.message.catalog.providers.length > 0)
if(response.data.responses.length > 0) {
response.data.responses = response.data.responses.slice(0, 1);
if(response?.data?.responses[0].message?.catalog?.providers){
response.data.responses[0].message.catalog.providers = response.data.responses[0].message.catalog.providers.slice(0, 3);
}
}

}
responseObject = {
status: true,
data: response.data,
Expand Down
32 changes: 21 additions & 11 deletions services/MapService.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import {Client} from "@googlemaps/google-maps-services-js";
import logger from '../utils/logger.js'
import polyline from '@mapbox/polyline';
import get_text_by_key from '../utils/language.js';
import Actions from "./Actions.js";

class MapsService {
constructor() {
this.client = new Client({});
this.session = {};
this.routes_image = null;
this.media_url = null;
}

/**
Expand Down Expand Up @@ -44,7 +45,7 @@ class MapsService {
// Save session if possible
if(this.session){
this.session.routes = routes;
this.routes_image = path;
this.media_url = await new Actions().download_file(path);
}

return routes.map(route => route.summary);
Expand Down Expand Up @@ -77,11 +78,12 @@ class MapsService {
logger.info(`Selecting route ${index.index}`);
if (this.session.routes && index.index >= 0 && index.index < this.session.routes.length) {
this.session.profile.selected_route = {
polyline: this.session.routes[index.index].overview_polyline.points
overview_polyline: this.session.routes[index.index].overview_polyline
}

// set route image in map instance
this.routes_image = await this.get_static_image_path([this.session.routes[index.index]]);
const static_image_url = await this.get_static_image_path([this.session.routes[index.index]]);
this.media_url = await new Actions().download_file(static_image_url);

return {
status: true,
Expand Down Expand Up @@ -160,15 +162,23 @@ class MapsService {
return Math.abs(deltaXt) < tolerance;
}

get_static_image_path(routes){
get_static_image_path(routes, markers=[]){
let polygon_path = '';
routes.forEach((route, index) => {
polygon_path+=`&path=color:${this.get_random_color()}|weight:${5-index}|enc:${route.overview_polyline.points}`;
})
let markers_path = '';

const route_image = `https://maps.googleapis.com/maps/api/staticmap?size=300x300${polygon_path}&key=${process.env.GOOGLE_MAPS_API_KEY}`;
return route_image;

if(routes.length === 0) return false;
else{
routes.forEach((route, index) => {
polygon_path+=`&path=color:${this.get_random_color()}|weight:${5-index}|enc:${route.overview_polyline.points}`;
})

markers.forEach((marker) => {
markers_path+=encodeURI(`&markers=color:blue|size:mid|${marker.location}`);
})

const route_image = `https://maps.googleapis.com/maps/api/staticmap?size=300x300${polygon_path}${markers_path}&key=${process.env.GOOGLE_MAPS_API_KEY}`;
return route_image;
}
}
}

Expand Down
2 changes: 0 additions & 2 deletions tests/unit/controllers/agent.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,6 @@ describe('API tests for getResponse() function', () => {
Body: message,
})
expect(response.text).to.be.a('string');
expect(response.text).to.contain('NH 48');
})

it('Should return routes between two points along with route image with raw request', async () => {
Expand Down Expand Up @@ -80,6 +79,5 @@ describe('API tests for getResponse() function', () => {
Body: message,
})
expect(response.text).to.be.a('string');
expect(response.text).to.contain('Lake');
})
})
35 changes: 33 additions & 2 deletions tests/unit/services/maps.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import * as chai from 'chai'
const expect = chai.expect
import MapsService from '../../../services/MapService.js'
import { describe } from 'mocha'
import logger from '../../../utils/logger.js'
const mapService = new MapsService()

describe('Should test the map service', () => {
Expand All @@ -11,7 +12,7 @@ describe('Should test the map service', () => {

let response = await mapService.getRoutes({source: source, destination: destination});

expect(response.routes).to.be.an('array');
expect(response).to.be.an('array');
});

it('Should test route fetching with strings', async () => {
Expand All @@ -21,7 +22,7 @@ describe('Should test the map service', () => {
let response = await mapService.getRoutes({source: source, destination: destination});
const routes_in_session = mapService.session.routes;

expect(response.routes).to.be.an('array').that.is.not.empty;
expect(response).to.be.an('array').that.is.not.empty;
expect(routes_in_session).to.be.an('array').that.is.not.empty;
expect(routes_in_session[0]).to.be.an('object').that.has.property('overview_polyline');
expect(routes_in_session[0]).to.have.property('navigation_url');
Expand Down Expand Up @@ -57,3 +58,33 @@ describe('Should test the map service', () => {
})
});

describe('Tests for get_static_image_path() to generate route image for given routes', ()=> {
it('Should return a path for a given route', async () => {
const source ='37.422391,-122.084845';
const destination = '37.411991,-122.079414';

await mapService.getRoutes({source: source, destination: destination});
const path = mapService.get_static_image_path(mapService.session.routes);

expect(path).to.be.a('string');
})

it('Should fail to return an image path for invalid routes', async () => {
const path = mapService.get_static_image_path([]);

expect(path).to.be.false;
})

it('Should return an image with markers if provided', async () => {

await mapService.getRoutes({source: 'Denver', destination: 'Yellowstone national park'});
const markers= [
{label: 'Denver', location: 'Denver'},
{label: 'Casper', location: '42.832293,-106.186146'}
]
const path = mapService.get_static_image_path(mapService.session.routes.slice(0, 1), markers);
logger.info(path);
expect(path).to.be.a('string').that.contains('markers');
})
})

0 comments on commit 5110dcb

Please sign in to comment.