GEE Animation, print thumbnail black - javascript

The following code is conceived to create a thumbnail image of landsat 7 images:
// Feature Collection
var aoi = geometry
print(aoi);
Map.addLayer(aoi);
var centroid = aoi.centroid(1)
print(centroid);
var coors = centroid.coordinates().getInfo()
var x = coors[0]
var y = coors[1];
Map.setCenter(x, y, 10);
// Elaborating the dates
// Getting Temperatures for Every Month
var period = ['-01-01', '-12-01'];
var years = [['1999', '2000'],
['2000', '2001'],
['2001', '2002'],
['2002', '2003'],
['2003', '2004'],
['2004', '2005'],
['2005', '2006'],
['2006', '2007'],
['2007', '2008'],
['2008', '2009'],
['2009', '2010'],
['2010', '2011'],
['2011', '2012'],
['2012', '2013'],
['2013', '2014'],
];
var add_period = function(year){
var start_date = period[0];
var end_date = period[1];
return [year[0] + start_date, year[1] + end_date];
};
var concatenate_year_with_periods = function(years, period){
return years.map(add_period);
};
var Dates = concatenate_year_with_periods(years, period);
/**********************************************************************
Landsat 7
***********************************************************************/
var visualization = {
bands: ['B4', 'B3', 'B2'],
min: 0.0,
max: 0.3,
};
var visualization_ = {
bands: ['B4_median', 'B3_median', 'B2_median'],
min: 0.0,
max: 0.3,
gamma: [0.95, 1.1, 1]
};
// Applies scaling factors.
var cloudMaskL7 = function(image) {
var qa = image.select('BQA');
var cloud = qa.bitwiseAnd(1 << 4)
.and(qa.bitwiseAnd(1 << 6))
.or(qa.bitwiseAnd(1 << 8));
var mask2 = image.mask().reduce(ee.Reducer.min());
return image
//.select(['B3', 'B4'], ['Red', 'NIR'])
.updateMask(cloud.not()).updateMask(mask2)
.set('system:time_start', image.get('system:time_start'));
};
var dataset = ee.ImageCollection('LANDSAT/LE07/C01/T1_TOA')
.filterDate('1999-01-01', '2020-12-31')
.filterBounds(aoi)
//.map(applyScaleFactors)
.map(cloudMaskL7)
.map(function(image){return image.clip(aoi)});
// Creating composites using median pixel value
var median_yearly_landsat_7 = function(start, end){
var dataset_ = dataset.filter(ee.Filter.date(start, end));
var median_yearly = dataset_.reduce(ee.Reducer.median());
return median_yearly;
};
var composite_name_list_l7 = ee.List([]);
var apply_monthly_composite = function(date_list){
var start = date_list[0];
var end = date_list[1];
var output_name = start + "TO" + end + "_LANSAT_7";
var composite = median_yearly_landsat_7(start, end);
composite_name_list_l7 = composite_name_list_l7.add([composite, output_name]);
Map.addLayer(composite, visualization_, output_name, false);
Export.image.toDrive({
image: composite,
description: output_name,
fileFormat: 'GeoTIFF',
crs : 'EPSG:4326',
folder : 'LANDSAT_LST_LAS_LOMAS',
region: aoi
});
return 0;
};
Dates.map(apply_monthly_composite);
/******************************************************************
// Animation gif
// Create RGB visualization images for use as animation frames.
/******************************************************************/
var text = require('users/gena/packages:text');
var annotated_collection_list = ee.List([])
var annotations = [
{position: 'left', offset: '0.25%', margin: '0.25%', property: 'label', scale: 1.5} //large scale because image if of the whole world. Use smaller scale otherwise
];
var create_annotated_collection = function(image_and_id) {
var img = image_and_id[0];
var image_id = image_and_id[1];
console.log(img);
console.log(image_id);
var img_out = img.visualize(visualization_)
.clip(aoi)//.paint(municipalities, 'FF0000', 2)
.set({'label': image_id});
Map.addLayer(img_out);
var annotated = text.annotateImage(img_out, {}, Bayern, annotations);
annotated_collection.add(annotated);
return 0;
};
var municipalities_geom = geometry;
var n = composite_name_list_l7.size().getInfo();
print(n);
for (var i = 0; i < n; i++) {
var img_info = ee.List(composite_name_list_l7.get(i));
print(img_info);
var img = ee.Image(img_info.get(0));
var img_id = ee.String(img_info.get(1));
var year = ee.String(ee.List(img_id.split("-").get(0)));
var month = ee.String(ee.List(img_id.split("-").get(1)));
var img_id_ = year.getInfo() + "_" + month.getInfo();
var img_out = img.visualize(visualization_)
.set({'label': img_id_});
var annotated = text.annotateImage(img_out, {}, municipalities_geom, annotations);
Map.addLayer(annotated);
var annotated_collection_list = annotated_collection_list.add(annotated)
}
var annotated_col = ee.ImageCollection(annotated_collection_list)
// Define GIF visualization parameters.
var gifParams = {
'region': geometry,
'dimensions': 254,
'crs': 'EPSG:32632',
'framesPerSecond': 1
};
// Print the GIF URL to the console.
print(annotated_col.getVideoThumbURL(gifParams));
// Render the GIF animation in the console.
print(ui.Thumbnail(annotated_col, gifParams));
However, this thumbnail appears black, but the images I load into the map project, are in the color visualization parameters that I need.
The geometry parameter is a Polygon I drew using the drawing tool. The coordinates are below:
Coordinates: List (1 element)
0: List (5 elements)
0: [-82.35277628512759,8.432445555054713]
1: [-82.314667459444,8.432445555054713]
2: [-82.314667459444,8.460632259476993]
3: [-82.35277628512759,8.460632259476993]
4: [-82.35277628512759,8.432445555054713]
Could someone tell my why the thumbnail appears black?

Related

Performing PCA per image over imageCollection in Google Earth Engine

I need to perform a PCA per image over a image collection. Then, I want to only keep Principle component axis 1, and add this as a band to every image within my image collection. Ultimately, I want to export a .csv file with GPS sampling locations at row headers and image ID as column headers with mean Principle component axis 1 as values. The idea behind doing this, is that I want a proxy (spectral heterogeneity) to use in further statistical analysis in R.
Here is the code I have thus far:
//Create an test image to extract information to be used during PCA
var testImage =ee.Image('LANDSAT/LC08/C01/T1_SR/LC08_168080_20130407')
.select(['B2', 'B3', 'B4', 'B5', 'B6', 'B7'],
['Blue', 'Green', 'Red', 'NIR', 'SWIR1', 'SWIR2']);
// Define variables for PCA
var region = Extent;
var scale = testImage.projection().nominalScale();
var bandNames = testImage.bandNames();
Map.centerObject(region);
// Function for performing PCA
function doPCA(image){
// This code is from https://code.earthengine.google.com/7249153a8a0f5c79eaf562ed45a7adad
var meanDict = image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: region,
scale: scale,
maxPixels: 1e9
});
var means = ee.Image.constant(meanDict.values(bandNames));
var centered = image.subtract(means);
// This helper function returns a list of new band names.
var getNewBandNames = function(prefix) {
var seq = ee.List.sequence(1, bandNames.length());
return seq.map(function(b) {
return ee.String(prefix).cat(ee.Number(b).int());
});
};
// [START principal_components]
var getPrincipalComponents = function(centered, scale, region) {
var arrays = centered.toArray();
var covar = arrays.reduceRegion({
reducer: ee.Reducer.centeredCovariance(),
geometry: region,
scale: scale,
maxPixels: 1e9
});
var covarArray = ee.Array(covar.get('array'));
var eigens = covarArray.eigen();
var eigenValues = eigens.slice(1, 0, 1);
var eigenVectors = eigens.slice(1, 1);
var arrayImage = arrays.toArray(1);
var principalComponents = ee.Image(eigenVectors).matrixMultiply(arrayImage);
var sdImage = ee.Image(eigenValues.sqrt())
.arrayProject([0]).arrayFlatten([getNewBandNames('sd')]);
return principalComponents
.arrayProject([0])
.arrayFlatten([getNewBandNames('pc')])
.divide(sdImage);
};
var pcImage = getPrincipalComponents(centered, scale, region);
return (pcImage);
}
// map PCA function over collection
var PCA = LandsatCol.map(function(image){return doPCA(image)});
print('pca', PCA);
Extent is my ROI, whereas LandsatCol is a preproccessed image collection. The code here produces an Error when trying to map the PCA over the image collection (second last line of code). The error reads: "Array: Parameter 'values' is required".
Any suggestions on how to deal with this? And how to add Principle component axis 1 as a band per image over the image collection?
I figured it out. The error "Array: Parameter 'values' is required" had to do with sparse matrices, which was a product of filtering, clipping and spesifying regions within to perform PCA. Earth Engine can not work with sparse matrices.
Here is the working code. LandsatCol is my preproccessed image collection.
// Display AOI
var point = ee.Geometry.Point([30.2261, -29.458])
Map.centerObject(point,10);
// Prepairing imagery for PCA
var Preped = LandsatCol.map(function(image){
var orig = image;
var region = image.geometry();
var scale = 30;
var bandNames = ['Blue', 'Green', 'Red', 'NIR', 'SWIR1', 'SWIR2'];
var meanDict = image.reduceRegion({
reducer: ee.Reducer.mean(),
geometry: region,
scale: scale,
maxPixels: 1e9
});
var means = ee.Image.constant(meanDict.values(bandNames));
var centered = image.subtract(means);
var getNewBandNames = function(prefix) {
var seq = ee.List.sequence(1, 6);
return seq.map(function(b) {
return ee.String(prefix).cat(ee.Number(b).int());
});
};
// PCA function
var getPrincipalComponents = function(centered, scale, region) {
var arrays = centered.toArray();
var covar = arrays.reduceRegion({
reducer: ee.Reducer.centeredCovariance(),
geometry: region,
scale: scale,
maxPixels: 1e9
});
var covarArray = ee.Array(covar.get('array'));
var eigens = covarArray.eigen();
var eigenValues = eigens.slice(1, 0, 1);
var eigenVectors = eigens.slice(1, 1);
var arrayImage = arrays.toArray(1);
var principalComponents = ee.Image(eigenVectors).matrixMultiply(arrayImage);
var sdImage = ee.Image(eigenValues.sqrt())
.arrayProject([0]).arrayFlatten([getNewBandNames('sd')]);
return principalComponents.arrayProject([0])
.arrayFlatten([getNewBandNames('pc')])
.divide(sdImage);
};
var pcImage = getPrincipalComponents(centered, scale, region);
return ee.Image(image.addBands(pcImage));
});
print("PCA imagery: ",Preped);

LookAt Matrix (Normalising Parallel Projection)

I am currently learning 3d computer graphics and normalising parallel projection into canocial view volume(LookAt Matrix as the familiar name). I try to implement it to the code using pure javascript as the parameter below.
var VRP = new Vertex(0,0,0);
var VPN = new Vertex(0,0,1);
var VUP = new Vertex(0,1,0);
var PRP = new Vertex(8,8,100);
var Window = [-1,17,-1,17];
var F = 1, B = -1;
Now, here is my attempt. I converted it first to canocial view volume.
NOTE: You can skip these steps directly to the code here and help me to fix the code to move the cube forward to camera(the screen) instead of moving away
1. Translate VRP to origin
var TVRP = [];
TVRP[0] = [1, 0, 0, -VRP.x];
TVRP[1] = [0, 1, 0, -VRP.y];
TVRP[2] = [0, 0, 1, -VRP.z];
TVRP[3] = [0, 0, 0, 1];
2. Rotate VRC such that n-axis,u-axis and v-axis align with z-axis, x-axis, and y-axis in order
function normalizeViewPlane(VPN) {
var unitVector = calculateUnitVector(VPN); //VPN/|VPN|
return normalizeVector(VPN,unitVector);
}
function normalizeViewUp(VUP, n) {
var dtProd = dotProduct(n,VUP);
var nVUP = new Vertex(n.x*dtProd, n.y*dtProd, n.z*dtProd);
VUP = new Vertex(VUP.x-nVUP.x, VUP.y-nVUP.y, VUP.z-nVUP.z);
var unitVector = calculateUnitVector(VUP); //VUP/|VUP|
return normalizeVector(VUP,unitVector);
}
function normalizeUVN(n,u) {
var V = crossProduct(n,u);
var unitVector = calculateUnitVector(V); //V/|V|
return normalizeVector(V,unitVector);
}
var n = normalizeViewPlane(VPN);
var v = normalizeViewUp(VUP, n);
var u = normalizeUVN(v, n);
var RVRC = [];
RVRC[0] = [u.x, u.y, u.z, 0];
RVRC[1] = [v.x, v.y, v.z, 0];
RVRC[2] = [n.x, n.y, n.z, 0];
RVRC[3] = [0, 0, 0, 1];
//Perform matrix multiplication 4x4 R.T(-VRP)
var res = multiplyMatrix4x4(RVRC, TVRP);
3. Shear DOP becomes parallel to z-axis
function shearDOP(PRP, uMaxMin, vMaxMin) {
var CW = new Vertex(uMaxMin,vMaxMin,0);
var mPRP = new Vertex(PRP.x,PRP.y,PRP.z);
return new Vertex(CW.x - mPRP.x, CW.y - mPRP.y, CW.z - mPRP.z);
}
var uMaxMin = (Window[1]+Window[0])/2;
var vMaxMin = (Window[3]+Window[2])/2;
var DOP = shearDOP(PRP,uMaxMin,vMaxMin);
var HX = (DOP.x/DOP.z)*-1;
var HY = (DOP.y/DOP.z)*-1;
var Hpar = [];
Hpar[0] = [1,0,HX,0];
Hpar[1] = [0,1,HY,0];
Hpar[2] = [0,0,1,0];
Hpar[3] = [0,0,0,1];
//res = R.T(-VRP)
res = multiplyMatrix4x4(Hpar,res);
4. Translate to front center of the view volume origin
var Tpar = [];
Tpar[0] = [1,0,0,-uMaxMin];
Tpar[1] = [0,1,0,-vMaxMin];
Tpar[2] = [0,0,1,-F];
Tpar[3] = [0,0,0,1];
//res=Hpar.R.T(-VRP)
res = multiplyMatrix4x4(Tpar,res);
5. Scale such that view volume becomes bounded by plane
var uMaxMin2 = 2/(Window[1]-Window[0]);
var vMaxMin2 = 2/(Window[3]-Window[2]);
var Spar = [];
Spar[0] = [uMaxMin2, 0, 0, 0];
Spar[1] = [0, vMaxMin2, 0, 0];
Spar[2] = [0, 0, 1 / (F - B), 0];
Spar[3] = [0, 0, 0, 1];
//res=Tpar.Hpar.R.T(-VRP)
res = multiplyMatrix4x4(Spar, res);
After convert it to the canocial view volume, I decided to multiply cube vertices to this final result transformation matrix.
//res=Spar.Tpar.Hpar.R.T(-VRP)
p = multiplyMatrix1x4(res,p);
//M is the parameter of cube vertice
M.x = p[0];
M.y = p[1];
M.z = p[2];
Thus, I had my cube is moving away from the camera as it is illustrated in image below.
However, I expect that cube is move closest to the camera instead of moving away as it is explained in image below(the object is house)
Am I missing the step or misunderstanding the algorithm of converting to canocial view volume? Which function or variable I shall modify to make the cube like the house above?
JSFiddle: https://jsfiddle.net/Marfin/hL2bmvz5/20/
Reference: https://telin.ugent.be/~sanja/ComputerGraphics/L06_Viewing_Part2_6pp.pdf
in general, if your cam is looking at the box and you want the cam to move towards the box, get the vector between cam and box and move the cam towards this direction:
cam += (box-cam)

Gradient fill empty in for loop

I'm drawing buttons on createjs canvas that have gradient fill and stroke. The number of buttons are drawn inside a for loop. Each section, as you will see in the fiddle, is drawn separately via function. but only the first function run draws the correct fill. The subsequent calls only draws the gradient stroke Jsfiddle
for (i = 0; i < db.length; i++) {
var btn = db[i];
var sdb = btn.split("_");
var blabel = sdb[0];
var battrib = sdb[1];
var bval = sdb[2];
var sid = sdb[3];
var tick = sdb[4];
var cptn = sdb[5];
var imageType = sdb[6];
var buttonSize = 90 + 10;
var bttn = new c.Shape();
bttn.graphics.beginLinearGradientFill([grad1, grad2], [.2, 1], 0, 0,0,50 ).setStrokeStyle(3).beginLinearGradientStroke([grad2, grad1], [.2, 1], 0, 0,0,50 ).drawRoundRect(x, y, 85, 35,5);
var label = new c.Text(blabel);
label.font = font;
label.color = '#000';
label.x = x+8;
label.y = y+6;
m1.addChild(bttn, label);
x+= buttonSize;
}s.update();
It seems to be working to me. Is it perhaps that you forgot to offset your buttons, so you're only seeing the first one? bttn.y = i*40
https://jsfiddle.net/gskinner/wqu4nzdq/12/

How to generate dynamic graph at runtime in amcharts..?

Following is the code of Multiple Value Axes:
In that we want to show the graph by using random data dynamically.
when we run this code nothing will be happened.
<script>
var chart;
var chartData = [];
// generate some random data, quite different range
function generateChartData() {
var firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 50);
var v = [];
var a = [];
for (var i = 0; i < 50; i++) {
var newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);}
for (var j = 1; j < 4; j++) {
v[j] = Math.round(Math.random() * 40 *j) + 100;
chartData.push({
date: newDate,
a + j.toString():v[j]
// a + j = stringValue - 0;
//var a[j]:v[j],
});
}
}
}
// this method is called when chart is first inited as we listen for "dataUpdated" event
function zoomChart() {
// different zoom methods can be used - zoomToIndexes, zoomToDates, zoomToCategoryValues
chart.zoomToIndexes(10, 20);
}
// create chart
AmCharts.ready(function() {
// generate some random data first
generateChartData();
// SERIAL CHART
chart = new AmCharts.AmSerialChart();
chart.marginTop = 0;
chart.autoMarginOffset = 5;
chart.pathToImages = "http://www.amcharts.com/lib/images/";
chart.zoomOutButton = {
backgroundColor: '#000000',
backgroundAlpha: 0.15
};
chart.dataProvider = chartData;
chart.categoryField = "date";
// listen for "dataUpdated" event (fired when chart is inited) and call zoomChart method when it happens
chart.addListener("dataUpdated", zoomChart);
// AXES
// category
var categoryAxis = chart.categoryAxis;
categoryAxis.parseDates = true; // as our data is date-based, we set parseDates to true
categoryAxis.minPeriod = "DD"; // our data is daily, so we set minPeriod to DD
categoryAxis.dashLength = 2;
categoryAxis.gridAlpha = 0.15;
categoryAxis.axisColor = "#DADADA";
// first value axis (on the left)
var valueAxis1 = new AmCharts.ValueAxis();
valueAxis1.axisColor = "#FF6600";
valueAxis1.axisThickness = 2;
valueAxis1.gridAlpha = 0;
chart.addValueAxis(valueAxis1);
// second value axis (on the right)
var valueAxis2 = new AmCharts.ValueAxis();
valueAxis2.position = "right"; // this line makes the axis to appear on the right
valueAxis2.axisColor = "#FCD202";
valueAxis2.gridAlpha = 0;
valueAxis2.axisThickness = 2;
chart.addValueAxis(valueAxis2);
// third value axis (on the left, detached)
valueAxis3 = new AmCharts.ValueAxis();
valueAxis3.offset = 50; // this line makes the axis to appear detached from plot area
valueAxis3.gridAlpha = 0;
valueAxis3.axisColor = "#B0DE09";
valueAxis3.axisThickness = 2;
chart.addValueAxis(valueAxis3);
var graph = [];
var v ;
for (var j = 1; j < 4; j++)
{
graph[j] = new AmCharts.AmGraph();
graph[j].valueAxis = valueAxis1; // we have to indicate which value axis should be used
graph[j].title = "red line";
v = a + j.toString();
graph[j].valueField = v;
graph[j].bullet = "round";
graph[j].hideBulletsCount = 30;
chart.addGraph(graph[j]);
}
// CURSOR
var chartCursor = new AmCharts.ChartCursor();
chartCursor.cursorPosition = "mouse";
chart.addChartCursor(chartCursor);
// SCROLLBAR
var chartScrollbar = new AmCharts.ChartScrollbar();
chart.addChartScrollbar(chartScrollbar);
// LEGEND
var legend = new AmCharts.AmLegend();
legend.marginLeft = 110;
chart.addLegend(legend);
// WRITE
chart.write("chartdiv");
});
</script>
<body>
<div id="chartdiv" style="width: 640px: hieght: 400px: overflow: hidden: text-align: left;">
</div>
</body>
It seems that you're looking to produce 4 graphs with random values.
If that is correct, there is a number of issues with your code:
The values for each of the graph need to be consolidated into a single data point for each category. So that we get one object per category:
[
{
date: '2015-01-01',
value1: 100,
value2: 200,
value3: 300,
value4, 400
},
{
date: '2015-01-02',
value1: 101,
value2: 201,
value3: 302,
value4, 404
},
// etc.
]
The best way to achieve that is to create an item with a date part, then generate a value for each graph and add them to the same object. Only then push that object into chart data array.
function generateChartData() {
var firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 50);
for (var i = 0; i < 50; i++) {
var newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
var item = {
date: newDate
};
for (var j = 1; j < 4; j++) {
item[j.toString()] = Math.round(Math.random() * 40 * j) + 100;;
}
chartData.push(item);
}
}
The same with adding actual graph objects. Just need to assign valueField to the same key as we did with data:
var v;
for (var j = 1; j < 4; j++) {
graph[j] = new AmCharts.AmGraph();
graph[j].valueAxis = valueAxis1; // we have to indicate which value axis should be used
graph[j].title = "red line";
v = j.toString();
graph[j].valueField = v;
graph[j].bullet = "round";
graph[j].hideBulletsCount = 30;
chart.addGraph(graph[j]);
}
Here's the complete working code:
var chart;
var chartData = [];
// generate some random data, quite different range
function generateChartData() {
var firstDate = new Date();
firstDate.setDate(firstDate.getDate() - 50);
for (var i = 0; i < 50; i++) {
var newDate = new Date(firstDate);
newDate.setDate(newDate.getDate() + i);
var item = {
date: newDate
};
for (var j = 1; j < 4; j++) {
item[j.toString()] = Math.round(Math.random() * 40 * j) + 100;;
}
chartData.push(item);
}
}
// this method is called when chart is first inited as we listen for "dataUpdated" event
function zoomChart() {
// different zoom methods can be used - zoomToIndexes, zoomToDates, zoomToCategoryValues
chart.zoomToIndexes(10, 20);
}
// create chart
AmCharts.ready(function() {
// generate some random data first
generateChartData();
// SERIAL CHART
chart = new AmCharts.AmSerialChart();
chart.marginTop = 0;
chart.autoMarginOffset = 5;
chart.zoomOutButton = {
backgroundColor: '#000000',
backgroundAlpha: 0.15
};
chart.dataProvider = chartData;
chart.categoryField = "date";
// listen for "dataUpdated" event (fired when chart is inited) and call zoomChart method when it happens
chart.addListener("dataUpdated", zoomChart);
// AXES
// category
var categoryAxis = chart.categoryAxis;
categoryAxis.parseDates = true; // as our data is date-based, we set parseDates to true
categoryAxis.minPeriod = "DD"; // our data is daily, so we set minPeriod to DD
categoryAxis.dashLength = 2;
categoryAxis.gridAlpha = 0.15;
categoryAxis.axisColor = "#DADADA";
// first value axis (on the left)
var valueAxis1 = new AmCharts.ValueAxis();
valueAxis1.axisColor = "#FF6600";
valueAxis1.axisThickness = 2;
valueAxis1.gridAlpha = 0;
chart.addValueAxis(valueAxis1);
// second value axis (on the right)
var valueAxis2 = new AmCharts.ValueAxis();
valueAxis2.position = "right"; // this line makes the axis to appear on the right
valueAxis2.axisColor = "#FCD202";
valueAxis2.gridAlpha = 0;
valueAxis2.axisThickness = 2;
chart.addValueAxis(valueAxis2);
// third value axis (on the left, detached)
valueAxis3 = new AmCharts.ValueAxis();
valueAxis3.offset = 50; // this line makes the axis to appear detached from plot area
valueAxis3.gridAlpha = 0;
valueAxis3.axisColor = "#B0DE09";
valueAxis3.axisThickness = 2;
chart.addValueAxis(valueAxis3);
var graph = [];
var v;
for (var j = 1; j < 4; j++) {
graph[j] = new AmCharts.AmGraph();
graph[j].valueAxis = valueAxis1; // we have to indicate which value axis should be used
graph[j].title = "red line";
v = j.toString();
graph[j].valueField = v;
graph[j].bullet = "round";
graph[j].hideBulletsCount = 30;
chart.addGraph(graph[j]);
}
// CURSOR
var chartCursor = new AmCharts.ChartCursor();
chartCursor.cursorPosition = "mouse";
chart.addChartCursor(chartCursor);
// SCROLLBAR
var chartScrollbar = new AmCharts.ChartScrollbar();
chart.addChartScrollbar(chartScrollbar);
// LEGEND
var legend = new AmCharts.AmLegend();
legend.marginLeft = 110;
chart.addLegend(legend);
// WRITE
chart.write("chartdiv");
});
#chartdiv {
width: 100%;
height: 500px;
font-size: 11px;
}
<script src="http://www.amcharts.com/lib/3/amcharts.js"></script>
<script src="http://www.amcharts.com/lib/3/serial.js"></script>
<script src="http://www.amcharts.com/lib/3/themes/light.js"></script>
<div id="chartdiv"></div>

Javascript accessing dynamic object properties

I am a novice programmer working with OpenJScad written in Javascript to build 3D models.
I am trying to figure out how to structure my code so that I can access an object's instance properties that are dynamically created with user input parameters. I have a parent Gear class with the following variable...
// Gear parent class
Gear = function(numTeeth, circularPitch, pressureAngle, clearance, thickness)
{
var pitchRadius = numTeeth * circularPitch / (2 * Math.PI);
I am making several Gear sub-classes that accept user parameters, ie...
// Spur Gear
function makeSpur(params)
{
var gear = new Gear(
params.spurTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.inputBore
);
if(params.inputBore > 0)
{
var inputBore = CSG.cylinder({start: [0,0,-params.thickness2], end:
[0,0,params.thickness2], radius: params.inputBore, resolution: 16});
gear = gear.subtract(inputBore).rotateX(90);
}
return gear;
...and then dynamically generating location coordinates based on the pitchRadius property of another Gear object...
// function main
var spurGear = makeSpur(params);
spurGear = spurGear.translate([-pinionGear.pitchRadius,0,0]);
Everything renders, except when I try to access the pitchRadius property from another Gear instance. Ive read about prototypes and accessing private / public properties, but I just can't figure out how to structure the code so that I can access instance properties in function main.
Here is the full code...
include("gears.jscad");
// Here we define the user editable parameters:
function getParameterDefinitions() {
return [
{ name: 'circularPitch', caption: 'Circular pitch:', type: 'float', initial: 5 },
{ name: 'pressureAngle', caption: 'Pressure angle:', type: 'float', initial: 20 },
{ name: 'clearance', caption: 'Clearance:', type: 'float', initial: 0 },
{ name: 'thickness', caption: 'Thickness of transmission gears:', type: 'float', initial: 5 },
{ name: 'spurTeeth', caption: 'Number of spur teeth:', type: 'int', initial: 32 },
{ name: 'pinionTeeth', caption: 'Number of pinion teeth:', type: 'int', initial: 14 },
{ name: 'bore', caption: 'Radius of shaft:', type: 'float', initial: 5 }
];
}
// Main function
function main(params)
{
// declare parts
spurGear = new makeSpur(params);
pinionGear = new makePinion(params);
// assemble parts
spurGear = spurGear.translate([-pinionGear.pitchRadius, 0, -20]); // BREAKS CODE
pinionGear = pinionGear.translate([-spurGear.pitchRadius, 0, 20]); // BREAKS CODE
return [spurGear,pinionGear];
}
// Spur Gear
function makeSpur(params)
{
var gear = new involuteGear(
params.spurTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.thickness,
params.bore
);
if(params.bore > 0)
{
var bore = CSG.cylinder({start: [0,0,-params.thickness], end: [0,0,params.thickness], radius: params.bore, resolution: 16});
gear = gear.subtract(bore).rotateX(90);
}
return gear;
}
// Pinion Gear
function makePinion(params)
{
var gear = new involuteGear(
params.pinionTeeth,
params.circularPitch,
params.pressureAngle,
params.clearance,
params.thickness,
params.bore
);
if(params.bore > 0)
{
var bore = CSG.cylinder({start: [0,0,-params.thickness], end: [0,0,params.thickness], radius: params.bore, resolution: 16});
gear = gear.subtract(bore).rotateX(90);
}
return gear;
}
// title: Gear
// author: Joost Nieuwenhuijse
// license: MIT License
/*
For gear terminology see:
http://www.astronomiainumbria.org/advanced_internet_files/meccanica/easyweb.easynet.co.uk/_chrish/geardata.htm
Algorithm based on:
http://www.cartertools.com/involute.html
circularPitch: The distance between adjacent teeth measured at the pitch circle
*/
function involuteGear(numTeeth, circularPitch, pressureAngle, clearance, thickness)
{
// default values:
if(arguments.length < 3) pressureAngle = 20;
if(arguments.length < 4) clearance = 0;
if(arguments.length < 4) thickness = 1;
var addendum = circularPitch / Math.PI;
var dedendum = addendum + clearance;
// radiuses of the 4 circles:
this.pitchRadius = numTeeth * circularPitch / (2 * Math.PI);
// this.getpitchRadius = function() {
//return pitchRadius;
//};
var baseRadius = this.pitchRadius * Math.cos(Math.PI * pressureAngle / 180);
var outerRadius = this.pitchRadius + addendum;
var rootRadius = this.pitchRadius - dedendum;
var maxtanlength = Math.sqrt(outerRadius*outerRadius - baseRadius*baseRadius);
var maxangle = maxtanlength / baseRadius;
var tl_at_pitchcircle = Math.sqrt(this.pitchRadius*this.pitchRadius - baseRadius*baseRadius);
var angle_at_pitchcircle = tl_at_pitchcircle / baseRadius;
var diffangle = angle_at_pitchcircle - Math.atan(angle_at_pitchcircle);
var angularToothWidthAtBase = Math.PI / numTeeth + 2*diffangle;
// build a single 2d tooth in the 'points' array:
var resolution = 5;
var points = [new CSG.Vector2D(0,0)];
for(var i = 0; i <= resolution; i++)
{
// first side of the tooth:
var angle = maxangle * i / resolution;
var tanlength = angle * baseRadius;
var radvector = CSG.Vector2D.fromAngle(angle);
var tanvector = radvector.normal();
var p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
points[i+1] = p;
// opposite side of the tooth:
radvector = CSG.Vector2D.fromAngle(angularToothWidthAtBase - angle);
tanvector = radvector.normal().negated();
p = radvector.times(baseRadius).plus(tanvector.times(tanlength));
points[2 * resolution + 2 - i] = p;
}
// create the polygon and extrude into 3D:
var tooth3d = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
var allteeth = new CSG();
for(var j = 0; j < numTeeth; j++)
{
var ang = j*360/numTeeth;
var rotatedtooth = tooth3d.rotateZ(ang);
allteeth = allteeth.unionForNonIntersecting(rotatedtooth);
}
// build the root circle:
points = [];
var toothAngle = 2 * Math.PI / numTeeth;
var toothCenterAngle = 0.5 * angularToothWidthAtBase;
for(var k = 0; k < numTeeth; k++)
{
var angl = toothCenterAngle + k * toothAngle;
var p1 = CSG.Vector2D.fromAngle(angl).times(rootRadius);
points.push(p1);
}
// create the polygon and extrude into 3D:
var rootcircle = new CSG.Polygon2D(points).extrude({offset: [0, 0, thickness]});
var result = rootcircle.union(allteeth);
// center at origin:
result = result.translate([0, 0, -thickness/2]);
return result;
}
I noticed that you are actually returning CSG object in the constructor, so try to use properties container described in OpenJSCAD User guide. According to the guide properties variable is intended to store metadata for the object.
This is an example from guide:
var cube = CSG.cube({radius: 1.0});
cube.properties.aCorner = new CSG.Vector3D([1, 1, 1]);
Additional comments:
You are returning different object then this in your constructor
If you will do something like this: gear = gear.rotateX(90); then you have new object
If you will use properties then metadata is cloned when you do transformation.

Categories