What determines Z-Index in a Google Geochart? - google-visualization

I am trying to plot yearly data on a geochart. I would like the most recent data on top, but for whatever reason, the earliest year is always on top in the actual visualization.
I have tried re-ordering the table to have the latest years as the first entries in the data with no effect.
I thought that maybe it was happening because I used a view to filter my data, but the filter is not reordering the items with the older ones first (so that shouldn't impact how it is displayed).
I do not want to filter out data since I use transparency to display all points. Here is some sample code that displays the same problem:
function drawVisualization() {
var data = new google.visualization.DataTable();
data.addColumn('number', 'Latitude');
data.addColumn('number', 'Longitude');
data.addColumn('number', 'Color');
data.addColumn('number', 'Output (MW)');
data.addRows([
[35, 135, 2, 334],
[35, 135, 1, 100],
[35.1, 135.1, 1, 100],
[35.1, 135, 1, 100],
[35, 135.1, 1, 100],
[34.9, 134.9, 1, 100],
[34.9, 135, 1, 100],
[35, 135.1, 1, 100],
]);
var geochart = new google.visualization.GeoChart(
document.getElementById('visualization'));
geochart.draw(data, {
colorAxis: {
'minValue': 1,
'maxValue': 2,
'values': [1, 2],
'colors': ['black','red'],
},
'markerOpacity': 0.5,
'region': 'JP'
});
}
I can change the values in column 2 or 3 (0-indexed), or I can change the order of the entries in to the data table, but I keep getting the same result. I have a feeling it always sticks bigger sized values in the back so you can still see the little values, but I'm wondering if there is any authoritative reference on it, or any way to get around it.
This is what it looks like no matter what I do:
What I want it to look like is as follows (manipulated the SVG manually to adjust the Z-order):

I played around with it for a bit, and I think you're right: it's automatically z-indexing the markers in size-order. If I read your intent correctly, you are looking to show some subset of years, and you want the markers to be z-indexed by years. I think you can accomplish that with some custom filtering: sort your data by location and year, then for every location, filter out every year with a smaller size than any of the newer years. Something like this should work:
// order by location and year (descending)
var rows = data.getSortedRows([0, 1, {column: 2, desc: true}]);
// parse the rows backwards, removing all years where a location has a newer year with a larger size value
// we don't need to parse row 0, since that will always be the latest year for some location
var size, lat, long;
for (var i = rows.length - 1; i > 0; i--) {
size = data.getValue(rows[i], 3);
lat = data.getValue(rows[i], 0);
long = data.getValue(rows[i], 1);
for (var j = i - 1; j >= 0 && lat == data.getValue(rows[j], 0) && long == data.getValue(rows[i], 1); j--) {
if (size < data.getValue(rows[j], 3)) {
rows.splice(i, 1);
break;
}
}
}
var view = new google.visualization.DataView(data);
view.setRows(rows);
Here's a working example based on your code: http://jsfiddle.net/asgallant/36AmD/

You are correct that the order of the markers is determined by the size, with the larger markers drawn first so they end up below the smaller markers, which is a convenience for most applications. If you wish to hide 'later' markers based on order, you'll have to do that another way, perhaps by hiding the rows of data.
Is there a reason it makes sense to hide data if it covers 'earlier' data? Perhaps an option could be added to disable this automatic reordering, especially if transparent colors are used to allow you to see through.

Try this, helped me in a project:
setTimeout(function () {
$('.google-visualization-table').css("z-index", "1");
}, 500);

Related

Replacing string values in a FeatureCollection with numbers in google earth engine

I have a FeatureCollection with a column named Dominance which has classified regions into stakeholder dominance. In this case, Dominance contains values as strings; specifically 'Small', 'Medium', 'Large' and 'Others'.
I want to replace these values/strings with 1,2,3 and 4. For that, I use the codes below:
var Shape = ee.FeatureCollection('XYZ')
var Shape_custom = Shape.select(['Dominance'])
var conditional = function(feat) {
return ee.Algorithms.If(feat.get('Dominance').eq('Small'),
feat.set({class: 1}),
feat)
}
var test = Shape_custom.map(conditional)
## This I plan to repeat for all classes
However, I am not able to change the values. The error I am getting is feat.get(...).eq is not a function.
What am I doing wrong here?
The simplest way to do this kind of mapping is using a dictionary. That way you do not need more code for each additional case.
var mapping = ee.Dictionary({
'Small': 1,
'Medium': 2,
'Large': 3,
'Others': 4
});
var mapped = Shape
.select(['Dominance'])
.map(function (feature) {
return feature.set('class', mapping.get(feature.get('Dominance')));
});
https://code.earthengine.google.com/8c58d9d24e6bfeca04e2a92b76d623a2

To split by date and event columns

I am trying to split by date and event columns. It is impossible to search for ". " some lines contain multiple sentences ending with ". " Also, some lines don't start with dates. The idea of ​​the script was to use a regexp to find lines starting with the fragment "one or two numbers, space, letters, period, space" and then replace "point, space" with a rare character, for example, "#". If the line does not start with this fragment, then add "#" to the beginning. Then this array can be easily divided into two parts by this symbol ("#") and written to the sheet.
Unfortunately, something went wrong today. I came across the fact that match(re) is always null. I ask for help in composing the correct regular expression and solving the problem.
Original text:
1 June. Astronomers report narrowing down the source of Fast Radio
Bursts (FRBs). It may now plausibly include "compact-object mergers
and magnetars arising from normal core collapse supernovae".[3][4]
The existence of quark cores in neutron stars is confirmed by Finnish
researchers.[5][6][7]
3 June. Researchers show that compared to rural populations urban red
foxes (pictured) in London are mirroring patterns of domestication
similar to domesticated dogs, as they adapt to their city
environment.[21]
The discovery of the oldest and largest structure in
the Maya region, a 3,000-year-old pyramid-topped platform Aguada
Fénix, with LiDAR technology is reported.
17 June. Physicists at the XENON dark matter research facility report
an excess of 53 events, which may hint at the existence of
hypothetical Solar axions.
Desired result:
Code:
function replace() {
const sheetName = "Sheet1";
const sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
const lr = sheet.getLastRow();
// const range = sheet.getRange(2, 4, lr - 1);
const range = sheet.getRange(100, 4, 5);
const arr = range.getValues();
const newArr = [];
const re = new RegExp("^([0-9]{1,2}\s[a-z]+\.)\s");
for (let i = 0; i < arr.length; i++) {
const match = arr[i][0].match(re);
if (match == null) {
let newEntry = "#" + arr[i];
newArr.push(newEntry);
} else {
// let newEntry = "#" + arr[i];
// newArr.push(newEntry);
}
}
// range.offset(0,1).setValues(newArr);
// console.log(newArr);
}
function breakapart() {
const ms = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('Sheet1');//Data Sheet
const osh = ss.getSheetByName('Sheet2');//Output Sheet
osh.clearContents();
const vs = sh.getRange(1, 1, sh.getLastRow(), sh.getLastColumn()).getDisplayValues().flat();
let oA = [];
vs.forEach(p => {
let f = p.split(/[. ]/);
if (!isNaN(f[0]) && ms.includes(f[1])) {
let s = p.slice(0, p.indexOf('.'));
let t = p.slice(p.indexOf('.')+2);
oA.push([s, t]);
} else {
oA.push(['',p]);
}
});
osh.getRange(1,1,oA.length,oA[0].length).setValues(oA);
}

How can I define the number of rows on cfspreadsheet object?

I am in the process of learning ColdFusion and I am trying to work with spreadsheets using spreadsheetFormatRows(spreadsheetObject, dataFormat, rangeOfRowsFormated)
How can I set the range to include all of the rows, except the header row, which is for column name? Is there a function that returns the number of the rows on cfspreadsheet object, so I can set the range to '2-rowCount'?
I tried spreadsheetFormatRows(theSheet, headerFormat, 2-50); and works fine and formats rows 2 to 50, but I don't want to have that hard-coded.
Thank you in advance.
The spreadsheet object has an attribute rowcount. You can do spreadsheetFormatRows(theSheet, format, "2-#theSheet.rowCount#");
<cfscript>
mySheet = spreadSheetNew("My Sheet");
spreadSheetAddRow(mySheet, "'Col. A','Col. B','Col. C'");
for(i=1; i <= RandRange(1, 100); i++){
spreadSheetAddRow(mySheet, "'Row A#i#','Row B#i#','Row C#i#'");
}
spreadSheetFormatRow(mySheet, {bold = true, fontsize = 24}, 1);
spreadSheetFormatRows(mySheet, {fontsize = 16}, "2-#mySheet.rowcount#");
cfheader(name = "Content-Disposition", value = 'inline; fileName="test.xls"');
cfcontent(type="application/vnd.ms-excel", variable="#spreadSheetReadBinary(mySheet)#");
</cfscript>
Try Online
Keep track of the number of rows as you populate them and save the value to a variable. Simpler yet, if they are query results, use the recordcount variable from cfquery.
Remember to add 1 so you format the last row.

Compact way to write a list of lists

I am writing a program that outputs a list of ordered lists of numbers. Say the output is as follows:
[1,1,1];
[1,1,2]
I would like to look at the output by eye and make some sense of it, but my output is hundreds to thousands of lines long. I would like to write the output in the following more compact format: [1,1,1/2], where the slash indicates that in the third slot I can have a 1 or a 2. So, for a longer example, [1/2, 1/3, 5, 8/9] would be the compact way of writing [1,1,5,8];[1,1,5,9];[1,3,5,8]; etc. Can anyone suggest a pseudocode algorithm for accomplishing this?
Edit: All of the lists are the same length. Also, I expect in general to have multiple lists at the end. For example {[1,1,2], [1,1,3], [1,2,4]} should become {[1,1,2/3], [1,2,4]}.
What'd I do is use a hash at each element in the first list. You'd then iterate through the remaining lists, and for each position in the other lists, you'd check against the hash in the first / original list for that index to see if you'd seen it before. So you'd end up with something like:
[1 : {1}, 1: {1, 3}, 5: {5}, 8: {8, 9}]
And then when printing / formatting the list, you'd just print each key in the hash, except you'd use slashes or whatever.
EDIT: Bad Psuedocode (python)(untested):
def shorten_list(list_of_lists):
primary_list = list_of_lists[0]
hash_values = [{} * len(primary_list)]
for i in range(len(list_of_lists)):
current_list = list_of_lists[i]
for j in range(current_list):
num = current_list[j]
if num not in hash_values[j]:
hash_values[j] = j
for i in range(len(hash_values)):
current_dict = hash_values[i]
print primary_list[i]
for key in current_dict:
if key != primary_list[i]:
print '/', key
Here's actual code to sort the lists the way you wanted. But maybe the most useful visualization would be a scatter plot. Import the data into your favorite spreadsheet, and plot away.
$(document).ready( function(){
var numbers = [
[1, 1, 5, 8],
[1, 1, 5, 9],
[1, 3, 5],
[1, 1, 5, 10, 15]];
$('#output').text(JSON.stringify(compactNumbers(numbers)));
});
function compactNumbers(numberlists){
var output = [];
for(var i = 0; i < numberlists.length; i++){
for(var j = 0; j < numberlists[i].length; j++) {
if(!output[j]) output[j] = [];
if($.inArray(numberlists[i][j], output[j]) == -1){
output[j].push(numberlists[i][j]);
}
}
}
return(output);
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div id="output"></div>

Couchdb: relational database capabilities

Let's assume that I have a list of 239800 documents like the following:
{
name: somename,
data:{age:someage, income:somevalue, height:someheight, dumplings_consumed:somenumber}
}
I know that I can index the doc by doc.data.age, doc.data.income, height, dumplings_consumed and get list of the doc that after giving a range for each parameters but how can I get a result for query like following:
List of the docs where age is between 25 and 30, income is less than $10 and height is more than 7ft?
Is there a way to get multiple indexes working?
Assuming all three of your example query parameters need to remain dynamic, you would not be able to do such a join with a single CouchDB query. The simplest strategy would be to emit an index that lets you narrow down the "biggest" aspect/dimension of your data, and then filter the rest out in your app's code or a _list function.
Now, for filtering on two aspects of numeric data, GeoCouch could potentially be used — it provides a generic 2-dimensional index, not just limited to latitude and longitude! So you would emit points that contain (say) "age" and "income" mapped to x and y. You'd then query a bbox with first two "between" parameters, and then you'd only have to filter out height on the app side.
Let's have a look at:
http://guide.couchdb.org/draft/views.html
You can search with any expression you want (javascript code) and index documents with it.
For example, by means of Futon, you can create a test database and add the two following documents based on your question:
{ "_id": "36fef0472fb7eec035c87e4f4b0381bf", "_rev": "12-4ef9014a3670a7e6acd58ad92d26fc1e", "data": { "age": 6, "income": 10, "height": 20, "dumplings_consumed": 5 }, "name": "joe" }
{ "_id": "36fef0472fb7eec035c87e4f4b038ffa", "_rev": "8-f0a0a51b830bf3d4bc3ec5697440792f", "name": "mike", "data": { "age": 27, "income": 9, "height": 78, "dumplings_consumed": 256 } }
You just have to go to your database still with Futon and create a temporary view with the following Map function:
function(doc) { var age, income, height; if (doc.name && doc.data && doc.data.age && doc.data.income && doc.data.height) { if ( doc.data.age > 25 && doc.data.age < 30 && doc.data.income < 10 && doc.data.height > 7) { emit(doc.name, doc.data); } } }
Just run and you get the result.
With a permanent view, first time the request is executed, the internal B-tree is built and it takes time. Further executions should be very fast even if documents are added to the database (as long as their number is a fraction of the totality)