Online KML to Google Spreadsheet Script - google-apps-script

Would like to use information into spreadsheets from a live Google Map (KML data). The code below handle it, but not as fast as required. It must handle up to 10 layers with about 2000 placemarks, line positions and/or polygon positions in the interesting (parameter "restrict") layer (or all layers). The size of the kml (xml) data is therefore often quite large.
Question 1: How (if possible) can caching and/or some other solution be implemented to handle about 2Mb xml data?
Question 2: How can what improvements be implemented to stream line the code, add flexibility to dynamically handle unknown (in advance) number of columns/rows and lower the execution time?
/**
* Fetch a Google Map and write the data to the spreadsheet (URL and layer).
*
* #param {url} input the URL associated with the Google Map (should contain &forcekml=1).
* #param {restrict} input the Layer name you want to restrict the data to.
* #return a range of data depending on the info in the map
* #customfunction
*/
function IMPORTMAP( url, restrict ) {
/* One key only allow 100k
var cache = CacheService.getPublicCache();
var cached = cache.get( "google-maps-xml" );
var cached = cache.getAll('?'); //Unknown keys in advance that also may change from time to time.
if ( cached != null ) {
return cached;
}*/
var txt = UrlFetchApp.fetch( url ).getContentText();
var sheet = SpreadsheetApp.getActiveSheet();
var width = sheet.getMaxColumns();
var height = sheet.getMaxRows();
var xmlDoc = XmlService.parse( txt );
var root = xmlDoc.getRootElement();
var atom = XmlService.getNamespace( 'http://www.opengis.net/kml/2.2' );
var xml2csv = [[height],[10]];
var labels = [], label = '', counter = 0, o = 0;
var documents = root.getChildren( 'Document', atom );
for( var i = 0; i < documents.length; i++ ) {
var Folders = documents[i].getChildren( 'Folder', atom );
for( var j = 0; j < Folders.length && j <= height; j++ ) {
if( Folders[j].getChild( 'name', atom ).getValue() == restrict ) {
var Placemarks = Folders[j].getChildren( 'Placemark', atom );
for( var k = 0; k < Placemarks.length; k++ ) {
var nodes = Placemarks[k].getChildren();
for( var l = 0; l < nodes.length; l++ ) {
var data = nodes[l].getChildren();
if( data.length > 0 ) {
for( var m = 0; m < data.length && counter <= width; m++ ) {
if( data[m].getAttribute( 'name' ) != null ) {
if( labels[ data[m].getAttribute( 'name' ).getValue().trim() ] == null ) {
labels[ data[m].getAttribute( 'name' ).getValue().trim() ] = counter;
xml2csv[ 0 ][ counter++ ] = data[m].getAttribute( 'name' ).getValue().trim();
}
}
if( data[m].getChild( 'value', atom ) != null ) {
xml2csv[ k + 1 ][ labels[ data[m].getAttribute( 'name' ).getValue().trim() ] ] = data[m].getChild( 'value', atom ).getValue().trim();
} else {
o = labels[ data[m].getName().trim() ];
if( o == null ) {
labels[ data[m].getName().trim() ] = counter;
o = counter;
xml2csv[ 0 ][ counter++ ] = data[m].getName().trim();
}
if( xml2csv[ k + 1 ] == null ) {
xml2csv[ k + 1 ] = new Array( counter );
}
xml2csv[ k + 1 ][ labels[ data[m].getName().trim() ] ] = data[m].getValue().trim();
}
}
} else {
if( label == '' )
label = nodes[l].getName().trim();
o = labels[ nodes[l].getName().trim() ];
if( o == null ) {
labels[ nodes[l].getName().trim() ] = counter;
o = counter;
xml2csv[ 0 ][ counter++ ] = nodes[l].getName().trim();
}
if( xml2csv[ k + 1 ] == null ) {
xml2csv[ k + 1 ] = new Array( counter );
}
xml2csv[ k + 1 ][ o ] = nodes[l].getValue().trim();
}
}
}
}
}
}
//cache.putAll( xml2csv, 1500 ); // cache for 25 minutes
return xml2csv;
}
Please note that code only handle specific kml data in its current form and may not be suitable for a wider audience.

Related

How can i get range number from A1notation?

This is a script to get a1notation from a number.
Conversely, I don't know how to get the number from a1notation !
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets()[0];
var range = sheet.getRange(1, 1, 2, 5);
// Logs "A1:E2"
Logger.log(range.getA1Notation());
i want to get (1, 1, 2, 5) from "A1:E2"
Here is an example of how to get the row and column from A1 notation.
function test() {
try {
let spread = SpreadsheetApp.getActiveSpreadsheet();
let sheet = spread.getSheetByName("Sheet1");
let range = "A1:E2";
range = getRange(range);
console.log(range);
console.log( sheet.getRange(range[0][0],range[0][1],(range[1][0]-range[0][0]+1),(range[1][1]-range[0][1]+1)).getValues() );
range = "F3";
range = getRange(range);
console.log(range);
console.log( sheet.getRange(range[0][0],range[0][1]).getValue() );
}
catch(err) {
throw "Error in test: "+err;
}
}
function getRange(range) {
try {
let ranges = range.split(":");
ranges = ranges.map( text => {
let cell = text.toUpperCase();
let column = 0;
let row = 0;
let char = null;
for( let i=0; i<cell.length; i++ ) {
char = cell.charCodeAt(i);
if( ( char > 64 ) && ( char < 91 ) ) {
if( row > 0 ) return null;
column = (26*column)+char-64;
}
else if( ( char > 47 ) && ( char < 58 ) ) {
if( column === 0 ) return null;
row = (10*row)+char-48;
}
}
if( ( column === 0 ) || ( row === 0 ) ) throw "Incorrect range";
return [row,column];
}
);
return ranges;
}
catch(err) {
throw "\nError in getRange: "+err;
}
};
8:47:47 AM Notice Execution started
8:47:49 AM Info [ [ 1, 1 ], [ 2, 5 ] ]
8:47:49 AM Info [ [ 'Goodbye World', 'Date/Time', 'a', 'b', 'c' ],
[ 1,
Mon Nov 07 2022 06:34:37 GMT-0800 (Pacific Standard Time),
1,
101,
201 ] ]
8:47:49 AM Info [ [ 3, 6 ] ]
8:47:49 AM Info 302
8:47:48 AM Notice Execution completed

MYSQL Join and create Distinct json

I have the following select
if ($result = $mysqli->query("SELECT
a.quoteID, a.quoteTitle , a.notes, c.web_path
FROM quotes a
INNER JOIN users_files b on b.user_id = a.quoteID
INNER JOIN files c on c.id = b.file_id ORDER BY quoteID ASC"))
{
$tempArray = array();
while($row = $result->fetch_object()) {
$tempArray = $row;
array_push($myArray, $tempArray);
}
echo json_encode($myArray);
}
which gets me information I need.
[{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"\/surplusAdmin3\/upload\/23.pdf"},{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"\/surplusAdmin3\/upload\/22.jpeg"},{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"\/surplusAdmin3\/upload\/21.jpeg"},{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"\/surplusAdmin3\/upload\/20.jpeg"},{"quoteID":"2","quoteTitle":"Kaitlin","notes":"Smith","web_path":"\/surplusAdmin3\/upload\/24.jpg"},{"quoteID":"8","quoteTitle":"Bryar2","notes":"Long","web_path":"\/surplusAdmin3\/upload\/17.png"},{"quoteID":"11","quoteTitle":"Avram","notes":"Allison","web_path":"\/surplusAdmin3\/upload\/4.jpg"}]
I now need to get my result as:
[{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path1":"\/surplusAdmin3\/upload\/23.pdf","web_path2":"\/surplusAdmin3\/upload\/22.jpeg","web_path3":"\/surplusAdmin3\/upload\/21.jpeg","web_path4":"\/surplusAdmin3\/upload\/20.jpeg"}]
I am not looking to re-do the MYSQL statement I just need to create a new array in the format I need.
I have the following code:
var currentQuoteID =0;
var dataChanged = [];
var arrayRow = -1 ;
var fileCount = 0;
dataReturned.forEach(function(e){
console.log(' current quote '+ currentQuoteID + ' = '+ e["quoteID"] + 'file count = '+ fileCount);
if ( currentQuoteID !== e["quoteID"]) {dataChanged.push(e); arrayRow = arrayRow + 1; fileCount = 1}
else
{
console.log('just add the filename array Row = ' +arrayRow );
// need to insert to the array arrayRow
// dataChanged.push('web_path'+fileCount,e["web_path"]);
toPush = 'web_path'+fileCount+'"'+':'+'"'+ e["web_path"];
var field = 'web_path'+fileCount;
var data = e["web_path"];
var tempArray = [field,data];
dataChanged.push(toPush);
dataChanged.field = data;
//dataChanged = dataChanged.concat(tempArray);
fileCount = fileCount +1;
}
currentQuoteID = e["quoteID"];
console.log('stuff '+ JSON.stringify(dataChanged));
});
The result I am getting is :
dataChanged = [{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"/surplusAdmin3/upload/23.pdf","Type":"image"},"web_path1","/surplusAdmin3/upload/22.jpeg"]
What I need is
dataChanged = [{"quoteID":"1","quoteTitle":"Title for 1","notes":"Deal no 1","web_path":"/surplusAdmin3/upload/23.pdf","Type":"image","web_path1","/surplusAdmin3/upload/22.jpeg"}]
But I cannot workout what I need to do here.
Got it working as follows:
// loop through and add field 'Type' can all be done in one loop.
dataReturned.forEach(function(e){
if (typeof e === "object" ){
e["Type"] = "other";
// console.log('stuff '+e['Type'][i] )
}
});
//loop through and workout the file type
dataReturned.forEach(function(e){
if (typeof e === "object" ){
var ext = e['web_path'].substr(e['web_path'].lastIndexOf('.') + 1);
if ( (ext === 'jpeg' ) || ( ext === 'jpg' )|| ( ext === 'png' )|| ( ext === 'gif' ) || ( ext === 'pdf' ))
{ e["Type"] = "image"; }
//console.log(e['web_path']);
// console.log('stuff '+ JSON.stringify(e))
}
});
// create a new array formated as needed
var currentQuoteID =0;
var arrayRow = -1 ;
var fileCount = 0;
dataReturned.forEach(function(e){
e["quoteID"] + 'file count = '+ fileCount);
if ( currentQuoteID !== e["quoteID"]) {dataChanged.push(e); arrayRow = arrayRow + 1; fileCount = 1}
else
{
//console.log('just add the filename array Row = ' +arrayRow );
// need to insert to the array arrayRow
// dataChanged.push('web_path'+fileCount,e["web_path"]);
var field = 'web_path'+fileCount;
var data = webPath+e["web_path"];
var tempArray = [field,data];
dataChanged[arrayRow]['web_path'+fileCount] = data
fileCount = fileCount +1;
}
currentQuoteID = e["quoteID"];
console.log('stuff '+ JSON.stringify(dataChanged));
});

AS3-Flash cs6 How to make numbers have a comma?

I am making a game that when you click on the Monster your score gets +1. But when your score goes over 1000 I would like it like this 1,000 rather than 1000. I am not sure how to do this as I have not learnt much action script. I have embed number and punctuation into the font. Here is my code so far:
var score:Number = 0;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Monster.addEventListener(TouchEvent.TOUCH_TAP, fl_TapHandler);
function fl_TapHandler(event:TouchEvent):void
{
score = score + 1;
Taps_txt.text = (score).toString();
}
Help will greatly appreciated.
You can do like that:
function affScore(n:Number, d:int):String {
return n.toFixed(d).replace(/(\d)(?=(\d{3})+\b)/g,'$1,');
}
trace(affScore(12345678, 0)); // 12,345,678
This may not be the most elegant approach, but I wrote a function that will return the string formatted with commas;
public function formatNum(str:String):String {
var strArray:Array = str.split("");
if (strArray.length >= 4) {
var count:uint = 0;
for (var i:uint = strArray.length; i > 0; i--) {
if (count == 3) {
strArray.splice(i, 0, ",");
count = 0;
}
count++;
}
return strArray.join("");
}
else {
return str;
}
}
I tested it on some pretty large numbers, and it seems to work just fine. There's no upper limit on the size of the number, so;
trace (formatNum("10000000000000000000"));
Will output:
10,000,000,000,000,000,000
So in your example, you could use it thusly;
Taps_txt.text = formatNum(String(score));
(This is casting the type implicitly rather than explicitly using toString();, but either method is fine. Casting just looks a little neater in function calls)
Use the NumberFormatter class:
import flash.globalization.NumberFormatter;
var nf:NumberFormatter = new NumberFormatter("en_US");
var numberString:String = nf.formatNumber(1234567.89);
trace("Formatted Number:" + numberString);
// Formatted Number:1,234,567.89
To show the score with comma, you can do like this : ( comments are inside the code )
var score:Number = 0;
var score_str:String;
var score_str_len:int;
Multitouch.inputMode = MultitouchInputMode.TOUCH_POINT;
Monster.addEventListener(TouchEvent.TOUCH_TAP, fl_TapHandler);
function fl_TapHandler(event:TouchEvent):void
{
score = score + 1;
score_str = score.toString();
score_str_len = score_str.length;
// here you can use score > 999 instead of score_str_len > 3
Taps_txt.text =
score_str_len > 3
// example : 1780
// score_str_len = 4
// score_str.substr(0, 4 - 3) = 1 : get thousands
// score_str.substr(4 - 3) = 780 : get the rest of our number : hundreds, tens and units
// => 1 + ',' + 780 = 1,780 : concatenate thousands + comma + (hundreds, tens and units)
? score_str.substr(0, score_str_len-3) + ',' + score_str.substr(score_str_len-3)
: score_str
;
// gives :
// score == 300 => 300
// score == 1285 => 1,285
// score == 87903 => 87,903
}
EDIT :
To support numbers greater than 999.999, you can do like this :
function fl_TapHandler(event:MouseEvent):void
{
score = score + 1;
score_str = score.toString();
Taps_txt.text = add_commas(score_str);
}
function add_commas(nb_str:String):String {
var tmp_str:String = '';
nb_str = nb_str.split('').reverse().join('');
for(var i = 0; i < nb_str.length; i++){
if(i > 0 && i % 3 == 0) tmp_str += ',';
tmp_str += nb_str.charAt(i);
}
return tmp_str.split('').reverse().join('');
/*
gives :
1234 => 1,234
12345 => 12,345
123456 => 123,456
1234567 => 1,234,567
12345678 => 12,345,678
123456789 => 123,456,789
1234567890 => 1,234,567,890
*/
}
Hope that can help you.

how to convert a string with html table like "<table><tr><td>12341</td></tr></table>" to JSON object?

How to convert a string containing HTML tags into JSON?
Any link or comment?
<?php
$string = "<table><tr><td>1231</td></tr></table>";
json_encode($string);
?>
Assuming you're running this in a browser:
var rows = document.getElementById('table-id').rows,
l = rows.length,
i = 0,
array = [];
for ( ; i < l; i++ )
{
array.push( getRowArray(rows[i]) );
}
function getRowArray ( row )
{
var array = [],
cells = row.childNodes,
l = cells.length,
i = 0;
for ( ; i < l; i++ )
{
cells[i].nodeType == 1 && array.push( cells[i].textContent );
}
return array;
}
Here's the fiddle: http://jsfiddle.net/cqzqs/

as3 multidimensional array

Im trying to make a multidimensional array but I obtain an error ("TypeError: Error #1010: A term is undefined and has no properties.").
var matriz:Array = new Array();
for(var p:Number = 0; p<2;p++ ){
for(var q:Number = 0; q<2;q++ ){
matriz[p][q] = 0;
}
}
what am I doing wrong?
Thanks in advance!
You need to create an array within matriz[p] before you can add an array (or anything else) into it.
You can achieve what you're attempting without errors like this:
var matriz:Array = [];
for(var p:Number = 0; p<2; p++)
{
// Create an array at matriz[p] if undefined.
if(matriz[p] == undefined) matriz[p] = [];
for(var q:Number = 0; q<2; q++)
{
matriz[p][q] = 0;
}
}
Essentially you were trying to do the same as this:
var object:Object = {};
object.nonexistantProperty.value = 10;
What Marty has said is correct, however I prefer removing the if condition and changing the code to the following:
var matriz:Array = [];
for(var p:Number = 0; p<2; p++) {
matriz[p] = [];
for(var q:Number = 0; q<2; q++) {
matriz[p][q] = 0;
}
}
public class cArray
{
private var DIM1CAP:uint=0;
private var DIM2CAP:uint=1;
private var DIM3CAP:uint=2;
private var DIM4CAP:uint=3;
private var DIM5CAP:uint=4;
public function cArray():void
{
// avoid the noid
}
// returns empty array of args.length dimensions
// 1st argument is dim 1 capacity; 2nd is dim 2, etc.
public function getArray ( ... args ):Array
{
var arr = new Array();
if ( paramsNotValid(args) )
{
return null;
}
switch (args.length)
{
case 2:
arr = get2DArray ( args[0], args[1] );
break;
case 3:
arr = get3DArray ( args[0], args[1], args[2] );
break;
case 4:
arr = get4DArray ( args[0], args[1], args[2], args[3] );
break;
case 5:
arr = get5DArray ( args[0], args[1], args[2], args[3], args[4] );
break;
default:
break;
}
return arr;
}
// returns empty 2d array of parameter specified capacity
private function get2DArray ( _1stDimCapacity:uint, _2ndDimCapacity:uint ):Array
{
var arr2d:Array = [];
var arr1d = new Array();
for ( var i:uint=0; i<_1stDimCapacity; i++ )
{
arr1d[_2ndDimCapacity-1] = undefined;
arr2d.push(arr1d);
arr1d = new Array();
}
return arr2d;
}
// returns empty 3d array of parameter specified capacity
private function get3DArray ( dim1Cap:uint,
dim2Cap:uint,
dim3Cap:uint ):Array
{
var arr3d = new Array();
for ( var i:uint=0; i<dim1Cap; i++ )
{
arr3d.push ( get2DArray ( dim2Cap, dim3Cap ) );
}
return arr3d;
}
// returns empty 4d array of parameter specified capacity
private function get4DArray ( dim1Cap:uint,
dim2Cap:uint,
dim3Cap:uint,
dim4Cap:uint):Array
{
var arr4d = new Array();
for ( var i:uint=0; i<dim1Cap; i++ )
{
arr4d.push ( get3DArray ( dim2Cap, dim3Cap, dim4Cap ) );
}
return arr4d;
}
// returns empty 5d array of parameter specified capacity
private function get5DArray ( dim1Cap:uint,
dim2Cap:uint,
dim3Cap:uint,
dim4Cap:uint,
dim5Cap:uint):Array
{
var arr5d = new Array();
for ( var i:uint=0; i<dim1Cap; i++ )
{
arr5d.push ( get4DArray ( dim2Cap, dim3Cap, dim4Cap, dim5Cap ) );
}
return arr5d;
}
//////////////////////////////////////////////////////
private function paramsNotValid ( args:Array ):Boolean
{
if ( args.length<2 || args.length>5 )
{
return true;
}
for ( var i:uint=0; i<args.length; i++ )
{
if ( ! ( args[i]>0 ) )
{
break;
}
}
if ( i < args.length )
{
return true;
}
return false;
}
}
}
public class cMain extends MovieClip
{
var cArr:cArray = new cArray;
public function cMain():void
{
var arr2d:Array;
var arr3d:Array;
var chessBrd_4d:Array;
var arr5d:Array;
// capacity of 10 games; 150 moves/gm; white's mv or black's;
// - piece positions; move in chess notation (index 32 of
// - last dimension); commentary (index 33 of last dimension)
chessBrd_4d = cArr.getArray(10,150,2,34);
// adding data
// - 4th game, 8th move, white's move, positions of pieces
chessBrd_4d[3][7][0][0] = 'd8';
chessBrd_4d[3][7][0][1] = 'b1';
// ...
// - positions of pieces up to last one
// ...
// - last piece pos
chessBrd_4d[3][7][0][31] = 'captured';
// - actual move in chess notation
chessBrd_4d[3][7][0][32] = 'nC4';
// - annotation
chessBrd_4d[3][7][0][33] = 'blocks b pawn, ' +
'opens diag for c1 bishop, ' +
'Justin Beiber is a putz, ' +
'the president is liar'
trace ( 'piece 0 is on square ' + chessBrd_4d[3][7][0][0]);
trace ( 'piece 1 is on square ' + chessBrd_4d[3][7][0][1]);
trace ( ' ... ' )
trace ( 'piece 31 has been ' + chessBrd_4d[3][7][0][31]);
trace ( 'move: ' + chessBrd_4d[3][7][0][32]);
trace ( chessBrd_4d[3][7][0][33]);
/*
trace results:
piece 0 is on square d8
piece 1 is on square b1
...
piece 31 has been captured
move: nC4
blocks b pawn, opens diag for c1 bishop,
Justin Beiber is a putz, the president is liar
*/
/*
trace ( chessBrd_4d.length ); = 10
trace ( chessBrd_4d [ 2 ].length ); = 150
trace ( chessBrd_4d [ 2 ] [ 4 ].length ); = 2
trace ( chessBrd_4d [ 2 ] [ 4 ] [ 1 ].length ); = 34
trace ( chessBrd_4d [ 2 ] [ 4 ] [ 0 ] .length); = 34
*/
}
}