Using the node.js google maps client library - node.js

I think this has to do with my limited experience with javascript.
I am using the node.js client library by Google , found here - https://github.com/googlemaps/google-maps-services-js
The example shows how to create a client object
var googleMapsClient = require('#google/maps').createClient({
key: 'your API key here'
});
And then how to run a gecode request and print out the results :
// Geocode an address.
googleMapsClient.geocode({
address: '1600 Amphitheatre Parkway, Mountain View, CA'
}, function(err, response) {
if (!err) {
console.log(response.json.results);
}
});
What I need to do is read a list of addresses from a file and build an array of objects for all of them .
I want my code to do something along the lines of :
create address-objects array
create a client object
open the text files
for each line in text files
geocode the line(address)
add the address and the results into an object and add it to the address-objects
I don't understand whether the googleMapsClient.geocode returns something , if yes how do I access it ? should I set it to some variable along the lines of :
var gc_results = googleMapsClient.geocode(param , ...)
Hope I was clear , thanks in advance
Jonathan.

You can access the response inside the callback function.
So, if you want to have this function to be called for each address, you can first define an empty array:
var gc_results = [];
Then you shall call the function for each address you get out of the file, something similar to this:
addresses.forEach(function(){
googleMapsClient.geocode({
address:
}, function(err, res) {
gc_results.push(res.json.results);
})
})
after this, you will have the gc_results array full with the information you need

Related

Getting coordinates from Yandex Map API Geocoder via multi-geocoder in NodeJs

I need to get coordidinates of set of addresses to display them on Yandex Map widget. There a lot of addresses, so I am going to get coordinates on the nodejs serverside. I have found package multi-geocoder, that looks exactly the solution for me. So I've written the example:
import MultiGeocoder from "multi-geocoder"
const geocoder = new MultiGeocoder({
provider: 'yandex',
coordorder: 'latlong',
lang: 'ru-RU',
apikey: 'My API key from https://developer.tech.yandex.ru/'
});
geocoder.geocode(['Москва'], {
apikey: 'My API key from https://developer.tech.yandex.ru/'
})
.then(function (res) {
console.log(res);
});
I got the response:
{
result: { type: 'FeatureCollection', features: [] },
errors: [ { request: 'Москва', index: 0, reason: 'Forbidden' } ]
}
I assume that something went wrong with apiKey, but cant figure out what exactly. How to get coordinates properly from nodejs script? Is it possible\legal at all?
Thank you.
If you have any problems with the API key, you should contact Yandex. Maps support. The problem may be with the key itself, or with your IP/domain. Only Yandex can determine the exact cause.
If you need to add points from the geocoder to the map, then it is easier to use geocoding in the JS API. It is enough to simply sequentially process the elements of the address array:
var searchArr = ['Москва, Фадеева, 5','Москва, Трубная, 31','Москва, Маросейка, 11'];
searchArr.forEach(function(item) {
ymaps.geocode(item, {
results: 1
}).then(function (res) {
var firstGeoObject = res.geoObjects.get(0),
coords = firstGeoObject.geometry.getCoordinates();
myMap.geoObjects.add(firstGeoObject);
});
});
If you display the data received from the data on a Yandex map and follow the other terms of use, the Yandex.Map API is available for free.
If at least one of the conditions must be violated, you should switch to a commercial license. Check out the fees here: https://tech.yandex.com/maps/tariffs/doc/jsapi/prices/index-docpage/

Respond with two files data on readFile - Node

I'm using node to respond clients with two files. For now, i'm using a endpoint for each file, cause i can't figure out how pass more than one in a row.
Here's the function that responds with the file:
exports.chartBySHA1 = function (req, res, next, id) {
var dir = './curvas/' + id + '/curva.txt'; // id = 1e4cf04ad583e483c27b40750e6d1e0302aff058
fs.readFile(dir, function read(err, data) {
if (err) {
res.status(400).send("Não foi possível buscar a curva.");
}
content = data;
res.status(200).send(content);
});
};
Besides that, i need to change the default name of the file, when i reach that endpoint, the name brings 1e4cf04ad583e483c27b40750e6d1e0302aff058, but i'm passing the content of 'curva.txt'.
Someone has any tips?
Q: How do I pass back contents of more than one file back to a user without having to create individual endpoints.
A: There are a few ways you can do this.
If the content of each file is not huge then the easiest way out is to read in all of the contents and then transmit them back as a javascript key-value object. E.g.
let data = {
file1: "This is some text from file 1",
file2: "Text for second file"
}
res.send(data);
res.end();
If the content is particularly large then you can stream the data across to the client, while doing so you could add some metadata or hints to tell the client what they are going to receive in the next moment and when is the end of file.
There is probably some libraries which can do the latter for you already, so I would suggest you shop around in github before designing/writing your own.
The former method is the easiest.

Bot Framework Node.js ad hoc message TO A SPECIFIC USER

I have been staring at this for hours and can't find a solution and that is even though by all suggestions it SHOULD be quite easy - https://learn.microsoft.com/en-us/bot-framework/nodejs/bot-builder-nodejs-proactive-messages.
I have created a simple code which will "register" the user and save their data in my cosmosDatabse on Azure. That works perfectly.
//ON "register" SAVE USER DATA AND SAY REGISTERED MESSAGE
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
//REGISTERED MESSAGE
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
But how can I then access that specific user and send him a message if, say, a condition is met? (in fact on HTTP request)
I am fairly certain we have to write the conversation ID or user ID somewhere. The question is where?
function startProactiveDialog(address) {
bot.beginDialog(address, "A notification!");
}
This is how simple I think it should be. But where do you specify the user then?
You've saved the address of the user inside of your database by saving it to session.userData.savedAddress. When the event triggers, perform a query to your database that checks for the users that meet two criteria.
They're registered to listen for the event
Their address has been saved inside of the database.
In your case, you can save a property to the session.userData object, a property that lists which events they're listening for. If you just need to send a message to the user, then you can simply use bot.loadSession(savedAddress) to ping the user.
Edit:
So instead of looking specifically by user ID, you should send a query to your CosmosDB that looks for entries that have a "listen-to" Boolean-type flag corresponding to the event.
You're not worrying about the user ID at first, you're just retrieving all entries with a query that would (broadly speaking) look like this:
SELECT * FROM BotState WHERE data LIKE 'listenForEvent=1.
So to setup your session.userData so that the above theoretical query would work, you would need to modify that snippet of code in your question to something like the following:
bot.dialog('adhocDialog', function(session, args) {
var savedAddress = session.message.address;
session.userData.savedAddress = savedAddress;
session.userData.listenForEvent = 1 // Our property we're going to look for.
session.endDialog("*Congratulations! You are now registered in our network! (goldmedal)*");
})
.triggerAction({
matches: /^register$/i
})
Actually, the savedAddress should be an instance of IAddress, and also, the function loadSession(address: IAddress, callback: (err: Error, session: Session) => void): void; and address(adr: IAddress): Message; under Message class all require IAddress as the parameter.
So first of all, you should save the entire address json object in cosmosDB for later using.
As botbuilder for Node.js is built on Restify or Express, you can build an addition route for your user to trigger and send proactive messages. The work flow could be following:
Guide user to register & Save the user's address object with the account mapping in your DB
Create a Route in Restify or Expressjs for trigger the proactive message:
server.get('/api/CustomWebApi', (req, res, next) => {
//find the user's address in your DB as `savedAddress`
var msg = new builder.Message().address(savedAddress);
msg.text('Hello, this is a notification');
bot.send(msg);
res.send('triggered');
next();
}
);
or if you want to leverage loadSession
server.get('/api/CustomWebApi', function (req, res, next) {
bot.loadSession(savedAddress, (err, session) => {
if (!err) {
session.send('Hello, this is a notification')
session.endConversation();
}
})
res.send('triggered');
next();
});
I created a users.json file, to which I save all the users. It works the way I need it to. I guess database would be better, but I don't really have a clue where to begin with that. Database is a whole new chapter I have not encountered yet, so it doesn't make sense to work on it when the project needs are resolved.

Very simple Node.js POST form inquiries

I have a simple node.js app using express static and nodemailer with a POST form that emails the filled fields to myself. My problems are very simple, I'm just quite new to Node so I can't find a way to do them.
My first problem is that I can't find a way to put all the form data into the email's text. Below, I am trying to store my form data in a JSON and call it in the email text, but it doesn't work. It has only correctly worked for me when I only used one variable (ex. just req.body.name) for the text. How can I format my data together in the email?
My second problem is that I can find a way to handle the app after the email is sent. I want it to redirect to a success page, as shown in the marked line, but it does not work. Instead, the page goes to /reqform and displays an error message saying success.html doesn't exist (it is in the same public folder as my html file). I believe the problem lies in my sendFile usage, but I'm not sure.
app.post('/reqform', urlencodedParser, function (req, res) {
response = {
name: req.body.name,
email: req.body.email,
phone: req.body.phone
};
var mailContent = {
from: 'myemail#gmail.com',
to: 'myemail#gmail.com',
subject: 'Service Request From req.body.name',
text: response //*** Problem #1
};
transporter.sendMail(mailClient, function (error, info) {
if (error) {
console.log(error);
} else {
res.sendFile("success.html"); //** Problem #2
}
});
})
Any help is greatly appreciated. Thanks!
The default string value for a JavaScript Object is just "[object Object]". If you want anything else, you'll have to be specific with how you want it represented.
For example, JSON is a text/string format that represents values like Objects. It's separate from the Object itself, so you'll need to convert to use it:
var mailContent = {
// ...
text: JSON.stringify(response)
}
Or, provide your own formatting:
var mailContent = {
// ...
text: `Name: ${response.name}\nEmail: ${response.email}\nPhone: ${response.phone}`
}
It might be better to use res.redirect() in this case, allowing the client/browser to request success.html via your application's static() middleware:
res.redirect('/success.html');
res.sendFile() doesn't collaborate with any static() middleware to know about your public folder. It expects you to provide a full path either directly or with its root option:
res.sendFile(path.join(__dirname, 'public/success.html'));

how to publish a page using node.js

I have just begun to learn node.js. Over the last two days, I've been working on a project that accepts userinput and publishes a ICS file. I have all of that working. Now consider when I have to show this data. I get a router.get to see if I am at the /cal page and..
router.get('/cal', function(req, res, next)
{
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
//console.log(ical.toString());
});
});
res.send(ical.toString());
// res.render('index', {
// title: 'Cal View'
// })
})
So when /cal is requested, it loops through my db and creates an ICS calendar ical. If I do console.log(ical.toString) within the loop, it gives me a properly formatted calendar following the protocol.
However, I'd like to END the response with this. At the end I do a res.send just to see what gets published on the page. This is what gets published
BEGIN:VCALENDAR VERSION:2.0
PRODID:calendar//EN
END:VCALENDAR
Now the reason is pretty obvious. Its the nature of node.js. The response gets sent to the browser before the callback function finishes adding each individual VEVENT to the calendar object.
I have two related questions:
1) Whats the proper way to "wait" till the callback is done.
2) How
do I use res to send out a .ics dynamic link with
ical.toString() as the content. Do I need to create a new view for
this ?
edit: I guess for number 2 I'd have to set the HTTP headers like so
//set correct content-type-header
header('Content-type: text/calendar; charset=utf-8');
header('Content-Disposition: inline; filename=calendar.ics');
but how do I do this when using views.
Simply send the response, once you got the neccessary data! You are not required to end or send directly in your route but can do it in a nested callback as well:
router.get('/cal', function(req, res, next) {
var db = req.db;
var ical = new icalendar.iCalendar();
db.find({
evauthor: 'mykey'
}, function(err, docs) {
docs.forEach(function(obj) {
var event2 = ical.addComponent('VEVENT');
event2.setSummary(obj.evics.evtitle);
event2.setDate(new Date(obj.evics.evdatestart), new Date(obj.evics.evdateend));
event2.setLocation(obj.evics.evlocation)
});
res.type('ics');
res.send(ical.toString());
});
});
I also included sending the proper Content-Type by using res.type.
Also: Don't forget to add proper error handling. You can for example use res.sendStatus(500) if an error occured while retrieving the documents.

Resources