I'm trying to implement a Google Earth controller via Websockets.
I have a simple echo websockets server in c#, when i type a value in a textbox it just sends it back to the page (I plan to be able to directly send data from the server later).
My script to initialize google earth is pretty standard and works:
<script type="text/javascript">
google.load("earth", "1");
function init() {
google.earth.createInstance('map3d', initCallback, failureCallback);
StartServer();
}
function initCallback(instance) {
ge = instance;
ge.getWindow().setVisibility(true);
// add a navigation control
ge.getNavigationControl().setVisibility(ge.VISIBILITY_AUTO);
// add some layers
ge.getLayerRoot().enableLayerById(ge.LAYER_BORDERS, true);
ge.getLayerRoot().enableLayerById(ge.LAYER_ROADS, true);
}
function failureCallback(errorCode)
{
}
</script>
(ge variable) is just the google earth instance.
Now in my server code, if I do:
ws.onmessage = function (evt)
{
inc.innerHTML += evt.data + '<br/>';
var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
lookAt.setLatitude(lookAt.getLatitude() + 10);
lookAt.setLongitude(lookAt.getLongitude() + 20);
ge.getView().setAbstractView(lookAt);
};
Everything works (earth rotates a tiny bit).
Now if I do:
ws.onmessage = function (evt)
{
inc.innerHTML += evt.data + '<br/>';
var lookAt = ge.getView().copyAsLookAt(ge.ALTITUDE_RELATIVE_TO_GROUND);
lookAt.setLatitude(lookAt.getLatitude() + evt.data); //Here i try to use the data received
lookAt.setLongitude(lookAt.getLongitude() + 20);
ge.getView().setAbstractView(lookAt);
};
I get exception : Error calling method on NPObject
I tried to split string, convert to number, but always get the same error.
inc.innerHTML += evt.data + '<br/>';
always works tho.
EDIT:
I also tried :
var i = parseInt(d); //Works
but then when i call l
lookAt.setLatitude(i); //Exception
Any help appreciated
Try replacing evt.data with (+evt.data), the unary plus operator forces the value to a number.
I don't know the Google Earth API but if getLongitude was returning a string for some weird reason then that could produce the behaviour you're seeing there (string + number is a number but string + string is a string).
Ok sorted, pretty stupid thing, server was sending extra characters.
Feels a bit bad to have this generic error on parsing, having something like "string format invalid" would be bit more meaningful.
I had the exact same problem and it turned out that the lat/long values were too precise, so I did this to turn it into the maximum 6 decimal precision equivalent:
function Blah(lat, lon) {
//because the GPS is ridiculously accurate and
//google earth is not, we need to round to six decimals at most
lat = Math.round(lat * 1000000) / 1000000;
lon = Math.round(lon * 1000000) / 1000000;
Related
I am writing the script behind a spreadsheet that has lots of durations on it in the format of (##:##.##) (ex 12:43.76). I need to write some code that converts this to just seconds. I wrote code that did the opposite, made seconds into that format. But when writing a custom formula for this, the .split method does not work.
function MTOS(input){
String(input);
if (typeof(input) != "string") {
Logger.log("Not a string")}
var array = input.split(":");
Logger.log('The original string is: "' + input + '"');
var min = Number(array[0]);
var sec = Number(array[1]);
Logger.log("min=" + min);
Logger.log("sec=" + sec);
var MIN = min*60;
Logger.log(MIN);
var ex = MIN+sec;
Logger.log(ex);
return ex;
}
This is what I have in the script editor. The input is the parameter from the spreadsheet when I write the formula in the sheet itself (ex - =MTOS(3:23.53)). When I run the function in the script editor, it gives me the error "TypeError: Cannot call method "split" of undefined. (line 5, file "MTOS")" and in sheets, it returns "Error : Result was not a number." I understand that this is happening because input is not defined in the function itself, so .split cannot work. But how else can I write the custom formula for sheets?
Thank you.
This seems to work for me: (Perhaps I'm misunderstanding the question).
function MTOS(input){
var iA = input.split(":");
var min = Number(iA[0]);
var sec = Number(iA[1]);
Logger.log('Seconds=%s',min * 60 + sec);
}
I'm looking to write a JavaScript function that will return the current BTC/USD exchange rate. I've done some research, but I just want something simple. It won't be used server-side for calculating values (obvious security implications), but just as a convenience for my users. I have 2 text fields, and when the user changes one of the values, it will update the other field.
Here is my code so far:
var getBTCRate = function(){ /* code here */ };
var btcprice = getBTCRate();
// update the BTC value as the USD value is updated
$("#usdvalue").keyup(function(ev){
var usdvalue = $("#usdvalue").val();
$("#btcvalue").val(usdvalue / btcprice);
});
// update the USD value as the BTC value is updated
$("#btcvalue").keyup(function(ev){
var btcvalue = $("#btcvalue").val();
$("#usdvalue").val(btcvalue * btcprice);
});
Plain and simple. In my research I haven't been able to find something that will do this, only a bunch of confusing APIs. Any help is much appreciated.
EDITED to fix a mistake in the code.
EDITED AGAIN to fix the position of the function declaration. Thanks to #RobG for pointing this out.
My first idea was to use JQuery load like this
$.get('https://www.google.com/search?q=btc+value', function(p) {
console.log(p);
});
but cross-origin rules stopped me.
Now, you can pay for a service that has an API, but I wanted to do it without having to pay. What I ended up doing is a server based solution. I use PowerBasic for my back end, with the SocketTools Library.
#COMPILE EXE
#DIM ALL
#Include "pbcgi.inc"
#Include "C:\bas_src\socketTools\v9.5\inc\cstools9.inc"
Function PBMain () As Long
Local btc As String
Local html As String
html= httpGet("https://www.google.com/search?q=btc+value")
' filter out just the current BTC value
' by looking for what is between the words "Bitcoin =" and "United States Dollar"
btc=Remain$(html,"Bitcoin =")
btc=Extract$(btc,"United States Dollar")
btc=Trim$(btc)
writeCGI "{"+jsonPad("btc")+":"+jsonPad(btc)+","+jsonPad("error")+":"+jsonPad("0")+"}"
END FUNCTION
'================================================================
' retrieve the page and return it as a string
Function httpGet(ByVal URL As String) As String
If IsFalse( HttpInitialize($CSTOOLS9_LICENSE_KEY) ) Then
Function="unable to init socket library"
Exit Function
End If
Local hClient As Dword
Local lpszURL As STRINGZ * 4096
Local lpszBuffer As STRINGZ * 100000
Local httpContents As String
Local httpPort As Long
Local httpOptions As Dword
Local nResult As Long
If LCase$(Trim$(Left$(URL, 8))) = "https://" Then
httpPort = 443
httpOptions = %HTTP_OPTION_SECURE Or %HTTP_OPTION_REDIRECT
Else
httpPort = 80
httpOptions = %HTTP_OPTION_REDIRECT
End If
lpszURL = URL & Chr$(0)
hClient = HttpConnect(lpszURL, _
httpPort, _
%HTTP_TIMEOUT, _
httpOptions, _
%HTTP_VERSION_10)
If hClient = %INVALID_CLIENT Then
Function = "Could not connect to: " & URL
Exit Function
End If
HttpSetEncodingType(hClient, %HTTP_ENCODING_NONE)
nResult = HttpGetText(hClient, lpszURL, lpszBuffer, 100000)
If nResult = %HTTP_ERROR Then
Function = "Error retrieving file."+Str$(nResult)
Else
' success
httpContents = lpszBuffer
Function =httpContents
End If
Call HttpDisconnect(hClient)
HttpUninitialize()
End Function
'================================================================
' pad string for json return
Function jsonPad(jstr As String) As String
Local i As Long
Replace Chr$(10) With " " In jstr
Replace Chr$(13) With " " In jstr
Replace "\" With "\\" In jstr
Replace $Dq With "\"+$Dq In jstr
For i = 0 To 9
Replace Chr$(i) With " " In jstr
Next
Function=$Dq+Trim$(jstr)+$Dq
End Function
On my web page, I call it using AJAX
function showBTC(){
$.ajax({
type: "POST",
url: "../cgi/btcGet.exe",
dataType: "json",
success: function(json){
if(json.error !== "0" ){
console.log( json.error );
return;
}
$("#btcValue").html( "current BTC value $"+json.btc+"<br><br>" );
}
});
}
I know that seems like a "too specific" answer, but it's how I do it.
EDIT 5/11/2020:
there is a Bitcoin API found at coindesk.com which greatly simplifies this process.
const api = 'https://apiv2.bitcoinaverage.com/indices/local/ticker/short?crypto=BTC&fiat=USD'
$.get(api, p => {
document.querySelector('pre').textContent = JSON.stringify(p, null, 2)
});
Result
{
"BTCUSD": {
"ask": 3594.5649555077953,
"timestamp": 1550284932,
"bid": 3591.715961836563,
"last": 3592.745617344171,
"averages": {
"day": 3583.13243402
}
}
}
So take your pick p.BTCUSD.ask // or bid or last
demo
I am working on Google Spreadsheet's project where I am adding/deleting TimeBased triggers through scripting.
All triggers fired will run one function, which will check its Id and run function accordingly.
I am saving starting trigger and saving it's id with this code.
function startTimer(rn) {
var triggerid = ScriptApp.newTrigger('mainFunction')
.timeBased()
.everyMinutes(1)
.create().getUniqueId();
SpreadsheetApp.getActiveSheet().getRange(1, 8).setValue(triggerid);
}
This functions save's trigger id in this format '6295890543675972447'
Now when trigger is fired and runs function 'mainFunction'.
Using this code I am trying to get the id of trigger.
function mainFunction(e) {
var triggerId = e.triggerUid;
Logger.log(triggerId);
}
At this point, I get the trigger id in this format '6.29589E+18'
If I try to change format with toString() method , the format changes to '6295890543675973000'
I cant match with both formatting to my saved id.
Any idea how can I get id back in format it was when adding trigger?
Thanks
I spent some hours on this, got some hair off.
And I found this page. It has a piece of code that strangely works when you pass e.triggerUid in it, and not when using it directly :
function getFileByTriggerId(triggerId){
var triggers = ScriptApp.getProjectTriggers();
for(var i =0; i<triggers.length; i++){
if(triggers[i].getUniqueId() == triggerId){
return triggers[i].getTriggerSourceId();
}
}
}
Hope it will help someone
When you request the id of a trigger object using its getUniqueId() method, the value returned is a numerical String.
However when a function is called by the trigger, the triggerUid property of the event object will be that string converted to a Number.
The issue is that the string id often (but not always) expresses an integer value larger than the maximum integer value Apps Script can handle safely. When this happens the conversion from string to number results in the zeros in the least significant digits.
So, to compare the two properly you just need to convert the string id to a number, this ensures the same zeroing will occur.
someTrigger.getUniqueId() === event.triggerUid
// false - because the left side is a string, and the right is a number.
Number(someTrigger.getUniqueId()) === event.triggerUid
// true - both sides are now numbers, the same number
someTrigger.getUniqueId() == event.triggerUid
// true - because using the standard comparison operator the string is automatically converted to a number in the background
Here's a note about strict v.s. standard comparison operations in Javascript.
Here's some Apps Script that demonstrates all of the above:
// Run onceOffClockTimer() and wait up to 1 min for the event to fire, then check your log. You might have to run it a few times to see a trigger id where the zeroing occurred
function onceOffClockTimer() {
var newTriggerId = ScriptApp.newTrigger('timerCallbackFn')
.timeBased()
.after(5000)
.create()
.getUniqueId();
}
function timerCallbackFn(triggerEvent) {
var eventTriggerId = triggerEvent.triggerUid,
triggers = ScriptApp.getProjectTriggers();
Logger
.log('### Event Object ###')
.log('id: %s', eventTriggerId)
.log('id type: %s', typeof eventTriggerId)
.log('id as String: %s\n', eventTriggerId.toString());
var largestSafeInteger = 9007199254740991; // Obtained by Number.MAX_SAFE_INTEGER in a Chrome console
Logger
.log('### Interesting Fact ###')
.log('In Apps Script the largest safe integer is: %s\n', largestSafeInteger);
for (var i = 0, x = triggers.length; i < x; i++) {
var triggerID = triggers[i].getUniqueId();
Logger
.log('### Trigger Object [%s] ###', i)
.log('id: %s', triggerID)
.log('id type: %s', typeof triggerID)
.log('id as Number: %s\n', Number(triggerID))
Logger
.log('### Comparisons ###')
.log('Strict (Trigger Event ID === Trigger ID) is %s ', eventTriggerId === triggerID)
.log('Strict (Trigger Event ID === Trigger ID as Number) is %s', eventTriggerId === Number(triggerID))
.log('Direct (Trigger Event ID == Trigger ID) is %s', eventTriggerId == triggerID);
}
}
Logger.log('' + triggerId);;;;
The trigger Id could be a large number (long 8 byte integer, or an even bigger number stored as an object), to overcome this, convert it to binary and then to 64 bit encoded string. This way you can safely store, and compare as well.
var v = Utilities.base64Encode(Utilities.newBlob(e.triggerUid).getBytes())
or to compare
if(Utilities.base64Encode(Utilities.newBlob(e.triggerUid).getBytes()) === Utilities.base64Encode(Utilities.newBlob(triggers[i].getUniqueId()).getBytes()))
As triggerId is a Number object and as such it is automatically converted to the exponential notation.
To handle this you can extend the Number.prototype to handle the correct representation.
Object.defineProperty(Number.prototype, 'toLongString', {
value: function() {
var parts = this.toString().split("e+");
var first = parts[0].replace('.', "");
var zeroes = parseInt(parts[1], 10) - (first.length - 1);
var longString = first;
for (var i = 0; i < zeroes; i++) {
longString += "0";
}
return longString;
}
});
function mainFunction(e) {
var triggerId = e.triggerUid.toLongString();
Logger.log(triggerId);
}
I had this same problem. The solution that worked for me was: toPrecision([numberOfDigits]).
For example:
([largenumber]).toPrecision(27)
Here is the source:
https://teamtreehouse.com/community/covert-scientific-notation-within-variable-to-javascript-number-when-scientific-notation-is-yet-unknown
I hope that this helps someone!
I would like to evaluate a string as an expression in Javascript. I'm reading the string from a JSON which is dynamic. So, the expression can be anything. Here is the pseudo code I'm using
var formula = {
"expression":"value * 9/5 + 32" //Dynamic JSON
}
var value = 26; // Dynamic value
var result = evaluateExpression(value, formula);
function evaluateExpression(value, formula) {
return eval(formula.expression);
}
This is how I've been using eval(). Is there any other alternative to this? I've also considered using Math.js, which I think is overkill for my requirements.
An alternative to eval would be to create a parser and evaluator in javascript. This is rather trivial, but a bit tedious. eval is mostly fine, unless you're going to evaluate strings provided by one user in the other user's browser. If this is the case, you'll have to write a parser (or generate it with a tool like PEG.js).
you could achieve the same using Function constructor
var formula = {
"expression":"value * 9/5 + 32" //Dynamic JSON
}
var value = 26; // Dynamic value
var result = evaluateExpression(value, formula);
alert(result);
function evaluateExpression(value, formula) {
return (new Function( 'value', 'return (' + formula.expression + ')' )(value));
//return eval(formula.expression);
}
How would it be a nice way of handling this?
I already thought on removing the comma and then parsing to float.
Do you know a better/cleaner way?
Thanks
parseFloat( theString.replace(/,/g,'') );
I don't know why no one has suggested this expression-
parseFloat( theString.replace(/[^\d\.]/g,'') );
Removes any non-numeric characters except for periods. You don't need custom functions/loops for this either, that's just overkill.
Nope. Remove the comma.
You can use the string replace method, but not in a one liner as a regexp allows.
while(str.indexOf(',')!=-1)str= str.replace(',','');
parseFloat(str);
Or to make a single expression without a regexp=
return parseFloat(str.split(',').join(''));
I'd use the regexp.
I don't have enough reputation to add a comment, but for anyone wondering on the performance for regex vs split/join, here's a quick fiddle: https://jsfiddle.net/uh3mmgru/
var test = "1,123,214.19";
var t0 = performance.now();
for (var i = 0; i < 1000000; i++)
{
var a = parseFloat(test.replace(/,/g,''));
}
var t1 = performance.now();
document.write('Regex took: ' + (t1 - t0) + ' ms');
document.write('<br>')
var t0 = performance.now();
for (var i = 0; i < 1000000; i++)
{
var b = parseFloat(test.split(',').join(''));
}
var t1 = performance.now();
document.write('Split/join took: ' + (t1 - t0) + ' ms');
The results I get are (for 1 million loops each):
Regex: 263.335 ms
Split/join: 1035.875 ms
So I think its safe to say that regex is the way to go in this scenario
Building on the idea from #kennebec, if you want to make sure that the commas are correct, and you don't want to replace commas, you could try something like this:
function myParse(num) {
var n2 = num.split(",")
out = 0
for(var i = 0; i < n2.length; i++) {
out *= 1000;
out += parseFloat(n2[i])
}
return out
}
alert(myParse("1,432,85"));
// Returns 1432085, as the comma is misplaced.
It may not be as fast, but you wanted alternatives :)
What about a simple function to solve most of the common problems?
function getValue(obj) {
Value = parseFloat( $(obj).val().replace(/,/g,'') ).toFixed(2);
return +Value;
}
The above function gets values from fields (using jQuery) assuming the entered values are numeric (I rather validate fields while user is entering data, so I know for sure field content is numeric).
In case of floating point values, if well formatted in the field, the function will return a float point value correctly.
This function is far from complete, but it quickly fix the "," (comma) issue for values entered as 1,234.56 or 1,234,567. It will return valid number as far the content is numeric.
The + (plus) sign in front of the variable Value in the return command is a "dirty trick" used in JavaScript to assure the variable content returned will be numeric.
it is easy to modify the function to other purposes, such as (for instance), convert strings to numeric values taking care of the "," (comma) issue:
function parseValue(str) {
Value = parseFloat( str.replace(/,/g,'') ).toFixed(2);
return +Value;
}
Both operations can even be combined in one function. I.e.:
function parseNumber(item,isField=false) {
Value = (isField) ? parseFloat( $(item).val().replace(/,/g,'') ).toFixed(2) : parseFloat( item.replace(/,/g,'') ).toFixed(2)
return +Value;
}
In such case, if function is called result = parseNumber('12,092.98'); it will parse the value as it is a String. But if called as result = parseNumber('#MyField', true); it will try to obtain the value from '#MyField'.
As I said before, such functions are far from complete, and can be expanded in many ways. One idea is to check the first character of the given parameter (string) and decide based on the string format where to obtain the value to be parsed (if 1st character is = '#' then it is an ID from a DOM object, otherwise, if it begins with a number, it must be a string to be parsed).
Try it... Happy coding.