Is there a way to get my current position (latitudes and longitudes, preferably in decimal form) from my phone's GPS data with a Google Apps Script?
Also, is it possible to turn GPS on and off or at least detect if it's on or off?
Here's what I try to do:
I go to places with my electric moped, and at every place I enter some data in a spreadsheet, such as date, distance since last position, which battery I'm using and more. One of the columns is my position. I currently enter it manually, but some positions are very common (for instance ”Work” and ”Home”) and it would be convenient if a script entered my position for me on edit.
I do that already, but it's very primitive at the moment. The only thing it does now, is check if it's a working day, and in that case, if my last position was ”Home”, it will enter ”Work”, and ”Home” if my last position was ”Work”. That takes care of the most common situations, but there are a few more places I go to often, so I figured that if I can get my current GPS coordinates from my phone, I could use them for figuring out if I'm at work, at home, or at one of the grocery stores I visit often or whatever.
I have searched for the answer, but all I can find is how to convert addresses to coordinates and the other way around, which obviously is not what I'm looking for at all.
I think we can try Geolocation API
Something like this (be mindful about handling user consent):
navigator.geolocation.getCurrentPosition(function(location) {
console.log(location.coords.latitude);
console.log(location.coords.longitude);
console.log(location.coords.accuracy);
});
https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API
Credits : Get GPS location from the web browser
Google Apps Script doesn't include a direct way the GPS data but this is possible by creating a Web App and using the Geolocation API.
Example:
I adapted the code frond on the above link to
Use it in a Google Apps Script web application
Log the latitude and longitud into a spreadsheet
Code.gs
function doGet(e) {
return HtmlService.createHtmlOutputFromFile('index');
}
function saveCoordinates(latitude,longitude){
const sheet = SpreadsheetApp.getActiveSheet();
const rowContents = [latitude,longitude];
sheet.appendRow(rowContents);
}
index.html
<!DOCTYPE html>
<html>
<head>
<base target="_top">
</head>
<body>
<button id = "find-me">Show my location</button><br/>
<p id = "status"></p>
<a id = "map-link" target="_blank"></a>
<script>
function geoFindMe() {
const status = document.querySelector('#status');
const mapLink = document.querySelector('#map-link');
mapLink.href = '';
mapLink.textContent = '';
function success(position) {
const latitude = position.coords.latitude;
const longitude = position.coords.longitude;
google.script.run.saveCoordinates(latitude,longitude);
status.textContent = '';
mapLink.href = 'https://www.openstreetmap.org/#map=18/' + `${latitude}\/${longitude}`;
mapLink.textContent = `Latitude: ${latitude} °, Longitude: ${longitude} °`;
}
function error() {
status.textContent = 'Unable to retrieve your location';
}
if(!navigator.geolocation) {
status.textContent = 'Geolocation is not supported by your browser';
} else {
status.textContent = 'Locating…';
navigator.geolocation.getCurrentPosition(success, error);
}
}
document.querySelector('#find-me').addEventListener('click', geoFindMe);
</script>
</body>
</html>
To use the above in your phone, you could add the link to the web application into your spreadsheet, so it can be openened on the phone web browser. In order to make this work you need to have an Internet connection enabled.
Related
Problem
I want to be able to monitor user activity on my google site using Apps Script, specifically which pages users are accessing. I can not use Google Analytics (it is not within my contract with Google). Apps Script has so far been able to return user ID (email address) of the user when a page is accessed, however I can not work out how to return which page(s) are activated by that user.
What I have done so far
I have created a web app and deployed / embedded it within to 2 pages in a test google site. It returns the values to a table in a linked google sheets file. Here is the script:
function doGet(event) {
return HtmlService.createHtmlOutputFromFile('UserLog.html');
}
function getPage(pageTitle) {
var user = Session.getActiveUser();
var ss = SpreadsheetApp.getActive();
var db = "Webapp Log";
var db_ss = ss.getSheetByName(db);
var now = new Date();
var page = pageTitle;
var values = [now,user,page,"User activated webapp",Session.getTemporaryActiveUserKey()];
db_ss.getRange(db_ss.getLastRow()+1, 1, 1, 5).setValues([values]);
};
The "UserLog.html" code is this:
<html>
<head>
<base target="_top">
</head>
<body>
<script>
var page = [Solution]
google.script.run.getPage(page);
</script>
</body>
</html>
And so far the return values look like this (with addresses anonymised):
Table Values
As you can see, the 'Page' field is blank.
What I need
Use the embedded web app to return the activated page URL OR to return another unique aspect of the activated page, for example the page title or "page 1", "page 2", etc. (all pages on the site have a unique title).
How can this be done? Is this even possible?
You can do it with e.parameters
E.g. incorporate a parameter page that you append at the end of the WebApp URL.
When you embedd the WebApp URL in each page, assign a unique value to page, like https://script.google.com/a/XXX/macros/s/XXX/exec?page=1, https://script.google.com/a/XXX/macros/s/XXX/exec?page=2
Now, in Apps Script you just have to slightly modify your doGet() function to retrieve the page:
function doGet(event) {
var page = event.parameter.page;
return HtmlService.createHtmlOutputFromFile('UserLog.html');
}
The rest depends on your preferences. The easiest would be to directly paste the value of page into the spreadsheet from the doGet() function - this will avoid passing the parameter to the html file and then, with google.script.run back to a .gs function.
I have a button on a spreadsheet that I want users to click that will run a script. The script modifies some protected ranges so I published it as a web app and set it to run as me.
I found these instructions explaining what I am trying to accomplish:
You can create a function with the doGet() reserved function name in your project, and publish the project as a Web App. A link is a GET request. When you click the link, a GET request will be made to the published URL of the Web App. Then the doGet() function will run. The GET request is the "event" and the doGet() function is triggered by the GET request event. doGet() "listens" for the GET request to be made. You can pass information to the doGet(e) function by adding an "event" parameter in the parenthesis. Typically the letter "e" is used, but any letter can be used
My questions are once I have the web app url, how do I create a link that goes to the web app and runs it? Right now I am using a button on the spreadsheet and assigning a script to it. Screenshot of this: https://imgur.com/OeMlczb But this is not working.
And here is the code I turned into a web app that need to be called and ran when the button is clicked:
function confirm(){
var ui = SpreadsheetApp.getUi();
var response = ui.alert('This will submit the timesheet. Do you want to continue?', ui.ButtonSet.YES_NO);
if(response == ui.Button.NO) return;
emailGoogleSpreadsheetAsPDF();
}
/* Email Google Spreadsheet as PDF */
function emailGoogleSpreadsheetAsPDF() {
// Send the PDF of the spreadsheet to this email address
var email = "email#gmail.com";
var exclA=['Timesheet','Note','Settings','Data'];//and others
var timeS=SpreadsheetApp.getActive().getSheetByName('Timesheet')
var ss=SpreadsheetApp.getActive();
var name=ss.getRange("Timesheet!J6").getValue();//trimmed the range down to match the getValue();
var tname=ss.getRange("Timesheet!J6").getValue();
var agency=ss.getRange("Timesheet!B4").getValue();//same here
var fldr=DriveApp.getFolderById('abc123thu8h7r8888tbgyru');
var fA=[];
var today=Utilities.formatDate(new Date(),Session.getScriptTimeZone(),"MM/dd/yyyy");
var subject=Utilities.formatString('%s has Submitted Their Timesheet and Notes',name);
var body=Utilities.formatString('This was submitted on %s',today);
var shts=ss.getSheets();
SpreadsheetApp.flush();//this may not be necessary...not sure
var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s_%s_%s_timesheet_notes.pdf', tname,agency,today));
fA.push(file)
for(var i=0;i<shts.length;i++) {
var sh=shts[i];
var name=sh.getName();
if(exclA.indexOf(name)==-1) {
sh.showSheet();
for(var j=0;j<shts.length;j++) {
if(shts[j].getName()!=name) {
shts[j].hideSheet();
}
}
SpreadsheetApp.flush();//this may not be necessary...not sure
var file=fldr.createFile(ss.getBlob().getAs('application/pdf')).setName(Utilities.formatString('%s_%s_%s_note.pdf', name,agency,today));
fA.push(file);
}
}
for(var i=0;i<shts.length;i++) {
if(exclA.indexOf(shts[i].getName())==-1) {
shts[i].showSheet();
}
}
timeS.showSheet();
GmailApp.sendEmail(email,subject,body, {attachments:fA});
for(var i=0;i<fA.length;i++) {
fA[i].setTrashed(true);
}
//CopyDataToNewFile();
}
function makeCopy() {
var ss =SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Note');
var dSheet = sheet.copyTo(SpreadsheetApp.openById(ss.getId()))
dSheet.showSheet()
};
function CopyDataToNewFile(targetSheetName,targetSsId, sourceSheetName,sourceSsId) {
var ss = SpreadsheetApp.openById('gnu84uw84nwutnst9ntrgbrn').getSheetByName('Timesheet');
var ssd = SpreadsheetApp.openById('h3487g8bg8ybw4gy8wytb').getSheetByName('Sheet1');
var therapist = ss.getRange('J6').getValues();
var thedate = ss.getRange('A10').getValues();
var theagency = ss.getRange('B4:C4').getValues();
var thepayperiod = ss.getRange('B6:C6').getValues();
var thecost = ss.getRange('E24').getValues();
var themileage = ss.getRange('E27').getValues();
ssd.getRange(ssd.getLastRow()+1,1,therapist.length,therapist[0].length).setValues(therapist);
ssd.getRange(ssd.getLastRow()+0,2,thedate.length,thedate[0].length).setValues(thedate);
ssd.getRange(ssd.getLastRow()+0,3,theagency.length,theagency[0].length).setValues(theagency);
ssd.getRange(ssd.getLastRow()+0,4,thepayperiod.length,thepayperiod[0].length).setValues(thepayperiod);
ssd.getRange(ssd.getLastRow()+0,5,thecost.length,thecost[0].length).setValues(thecost);
ssd.getRange(ssd.getLastRow()+0,6,themileage.length,themileage[0].length).setValues(themileage);
}
I am just unsure how to structure this and any help is greatly appreciated, thank you!
A Link to a Webapp is just a bookmark in my browser
Here's a simple example of a webapp that I just put together for you.
html:(This is the user interface) It's all done in html and so there is a wide latitude of techniques that can be deployed here depending upon your knowledge of html, javascript, css etc.....
But this one just has one text box and one button and I didn't use any JQuery or other libraries. It's just basic old school JavaScript and html.
<!DOCTYPE html>
<html>
<head>
<base target="_top">
<script>
window.onload=function() {
document.getElementById('hdg1').innerHTML="You are now connected to the server";
};
function runMyFunction() {
var txt=document.getElementById('txt1').value;
if(!txt) {
alert("You have not entered any text");
}else{
google.script.run
.withSuccessHandler(function(msg){
document.getElementById('msg1').innerHTML=msg;
})
.myFunction(txt);
}
}
console.log('aq4.html');
</script>
</head>
<body>
<h1 id="hdg1"></h1>
<input type="text" id="txt1" placeholder="Put some text here" />
<input type="button" value="myfunction" onclick="runMyFunction()" />
<div id="msg1"></div>
</body>
</html>
Google Script:
function myFunction(txt) {
return "<h1>I have received your message and ran myFunction():</h1><strong>" + txt + "</strong>";
}
function doGet() {
return HtmlService.createHtmlOutputFromFile('aq4');
}
The link that I was referring to is the simply the url of the web app and all you have to do is bookmark that url and then click on the bookmark from your browser. I describe that a little in this video
You have a problem because code runs 'as the user' (who has no access rights).
You talk as if deploying as a WEB APP to make code run with developer permissions is the only way to go. First of all, I know another way to go.
Deploy as WEB App may prove unsuitable or complex, for many situations, like yours, and mine.
I am new for a month working and the way I have found to execute code 'as ME' -developer- is having ME fire execution. That may get accomplished with installable triggers.
Please find in: Apps Scrip Guides listed under "Restrictions" this point. It says nothing about advantages. I found this point an advantage, because I don't need to give access rights to users on sensible tables !!!
Per my testings, I can .openById(...) and modify objects on which active user has no access rights. Just login as Permissions holding user to https://script.google.com/home and create the installable triggers needed. Code will then run as the user creating the trigger, with all his permissions.
I'm still looking for a simpler way to inherit access rights from developer (...or maybe an ad-hoc created user) to the code written. Or to stop some 'editors' of a Spreadsheet from viewing & modifying the code. But this is for another question.
I’m trying to access Maps from a script bound to a Google form. The problem i’m having is that when debugging the script, it accesses Maps so often that i’m Running into quota limits. I have a Maps API key but do not have a client ID so can’t get Maps.setAuthenication(clientID,Key) to work. I’m doing this for a Scout Troop so don’t want to have to pay to access maps.
Can anyone help?
I was subsequently asked to post my code, and so here it is:
function setLocation(){
var whereString;
var theDuration;
var theDistance;
var theRoute;
var theDirections;
var theTravelString;
// this Sets the Where: Tab on the form
whereString = 'Where: ' + gLocation;
theItemArray = gSignupForm.getItems();
theItemArray[kWhereItem].setTitle(whereString);
//this gets the directions to the location
Maps.setAuthentication('','ABCDEFGHIJKLMNOP');
//Obviously Im'm not going to post the true key
theDirections = Maps.newDirectionFinder()
.setOrigin('7101 Shadeland Ave, Indianapolis, IN 46256')
.setDestination(gLocation)
.setMode(Maps.DirectionFinder.Mode.DRIVING)
.getDirections();
theRoute = theDirections.routes[0];
theDuration = theRoute.legs[0].duration.text;
theDistance = theRoute.legs[0].distance.text;
theTravelString = Utilities.formatString('Travel Considerations: The estimated travel distance is %u miles. ',theDistance);
theTravelString += 'The estimated travel time is ' + theDuration;
theItemArray[kTravelItem].setTitle(theTravelString);
}
One solution to limit the use of low-quota services is to avoid calling those services when the desired information has not changed.
For example, through the use of CacheService, you can dramatically reduce your calls to the Maps API, debugging session or not:
var cache = CacheService.getScriptCache();
function setLocation() {
// Try to find the route for this location if it's still available
var storedRoute = cache.get(gLocation);
if (!storedRoute) {
// No route for this value of the key gLocation was found. Query as normal.
...
theRoute = theDirections.route[0];
// Cache this route for future uses, for the maximum of 6hr).
cache.put(gLocation, JSON.stringify(theRoute), 21600);
} else {
// We have this exact stored route! Convert it from the stored string.
theRoute = JSON.parse(storedRoute);
}
theDuration = ...
...
Your "gLocation" variable may be directly usable as a cache key. If not, you'll need to make it useable by encoding it. The max length key is 250 characters. This single-parameter caching assumes your directions all have a fixed endpoint, as shown in your example code. If both endpoints vary, you'll have to construct a cache key based on both values.
Related question: Maps direction Quota limits
Im using the Google Places Autocomplete API, to have an input in which users type a city and get suggestions for which place they are searching for.
I need to get not only the name of the place but the Latitude and Longitud of the place for then centering a google map there.
The problem is that Google Places Autocomplete API just returns description of the place but not the coordinates, at least with what i tried.
Anybody knows if there is a way to get the Latitud & Longitud in the same request?
Many thanks to any help :')
All the information you are looking for can be found inside the Place Result. This is what your code would look like:
var input = document.getElementById("address");
var autocomplete = new google.maps.places.Autocomplete(input, {types: ["geocode"]});
autocomplete.addListener("place_changed", function() {
var placeResult = autocomplete.getPlace();
var name = placeResult.name;
// The selected place's position
var location = placeResult.geometry.location;
// The preferred viewport when displaying this Place on a map.
// This property will be null if the preferred viewport for the Place is not known.
var viewport = placeResult.geometry.viewport;
// This is assuming your google map is saved in a variable called myMap
if (viewport) {
myMap.fitBounds(viewport)
}
else if (location) {
myMap.setCenter(location)
}
});
I recently created a jQuery plugin that makes interacting with Google Maps Autocomplete a breeze. This would be the code to center a map every time a place was selected:
$("#address").geocomplete({
map: myMap
});
The plugin will also allow you to autofill an address form, adds custom styling, provides callbacks and fixes an annoying scrolling issue.
I have a google visualization table that I'm publishing in a web app.
Background:
I run a script that lists all the documents in a google folder in a spreadsheet. I then push that list into the google table I have published in the web app.
The need:
I want to manage those that same list of documents directly from the web app. I want to be able to to move the document from one folder to another when I select the applicable row on the table.
Two things I have accomplished:
I have a script that will move the document to a specific folder on
the google drive using it's doc id.
I have an event listener on the table so that when you click a row and
then click the delete icon, you get a prompt that asks, "are sure
you want to archive [enter document name]?" When I click ok, I get my
test prompt that says "document archived". When I click no, I get my
test prompt that says "request cancelled". So from this, I know I
have the appropriate code (at least that's how it seems).
What I'm struggling with:
I can't seem to get the codes above to work together. The event listener is providing me the url of the document which I have parsed to give me only the id. This is what I was hoping to use to get the rest of the code to run, but I think because I'm trying to interact with the server-side from the client-side, it's not working. Can anyone help me figure it out? I know that I need to use google.script.run.withSuccessHandler when running a server side script from the client side, but I don't know how it applies to this case the docid I need is being collected on table select. Any help is appreciated and I hope the above makes sense!
// Draw Dashboard
h2dashboard.bind([h2stringFilter, h2typeFilter], [h2chart]);
h2dashboard.draw(h2dataView);
google.visualization.events.addOneTimeListener(h2chart, 'ready', function() {
google.visualization.events.addListener(h2chart.getChart(), 'select', function() {
var selection = h2chart.getChart().getSelection();
var dt = h2chart.getDataTable();
// Get Value of clicked row
if (selection.length) {
var item = selection[0];
var docurl = dt.getValue(item.row, 1);
var docname = dt.getValue(item.row, 0);
var source = dt.getValue(item.row, 3);
// When button is clicked, show confirm box with value
$(document).ready(function() {
$("#hu2archive").on("click", function() {
var answer = confirm("Are you sure you want to archive " + docname + "?");
if (answer === true) {
var archive = DriveApp.getFolderById("FOLDER ID");
var docid = docurl.match(/[-\w]{25,}/); // This is where I'm grabbing the value from the row.
var doc = DriveApp.getFileById(docid);
doc.makeCopy(archive).setName(doc.getName());
source.removeFile(doc);
alert(docname + " has been archived!");
} else {
alert("Request cancelled");
}
});
});
}
});
});
I just got it! What I was having a hard time understanding was how to pass a variable from the client side to code.gs. I have only run a script in code.gs from the client side on button submit but never passed anything back.
So I ended up changing my code to the below which passes the variable I need into a successhandler where archiveDoc is the function in my code.gs and docurl is the name of the variable I need to pass from the eventlistener.
if (answer === true) { google.script.run.withSuccessHandler(onSuccess).withFailureHandler(err).archiveDoc(docurl);
I'm still new to coding so I just learned something new! So thanks Spencer Easton. I did in fact answer my own question.