Can you use if statements in .nest(). If not what is the best way to assign labels to array elements so they fit in several groups - d3.js

Is it possible to use if statements within .nest()? I'm trying to populate a column with several keys, but instead of looking for a name I want to assign a name/value to individual rows based on whether the value in the column is within a certain range of numbers. I have looked at the D3 Nest Tutorial and examples and I'm still having a hard time. This is the code I have at the moment, which isn't working. Any help would be greatly appreciated!
fireballData.forEach( d => {
years.forEach(year => {
const latitude = d['Latitude (deg.)'];
const impactEnergy = d['Calculated Total Impact Energy (kt)'];
const impactTest = +impactEnergy;
const impactLevel = nest('impactTest')
.key(function(d) { if (d.impactTest < .5) {return "impactA" }});
// .sortKeys(d3.ascending)
// .key(function(d) { return d.impactEnergy; })
// .sortKeys(function(a,b) {
// return impactEnergy.indexOf(a) - impactEnergy.indexOf(b); })
// .sortValues(function(a,b) { return ((a.who < b.who)
// ? -1
// : 1);;
const longitude = d['Longitude (deg.)'];
const impactYear = d['PeakBrightnessDate_TimeUT'];
const row = {
//year,
impactLevel,
impactEnergy,
latitude,
longitude,
impactYear
};
console.log(row);
});
});
Just to be a bit more clear, I want to assign five or six impact levels to the column impactLevel based on how big of a value impactEnergy is. This would then leave each row with the impactLevel id (ie impactLevel1) followed by the rest of data of that object. Any solustions or suggestions are great appreciated! I am still very new to d3 and I am still learning the in's and out's.

So for what I was trying to accomplish using .nest wasn't a good solution. The better way of going about creating the new array was to use .map.
export const loadAndProcessData = () =>
Promise
.all([
csv('data.csv')
])
.then(([fireballData]) => {
const minYear = 2015;
const maxYear = 2100;
const years = range(minYear, maxYear + 1);
const data = [];
// Special thanks to Darshit Shah for helping me figure this bit out
const showData = fireballData.map(d =>{
const impactYear = d['PeakBrightnessDate_TimeUT'];
const latitude = d['Latitude (deg.)'];
const longitude = d['Longitude (deg.)'];
const impactEnergy = +d['Calculated Total Impact Energy (kt)'];
const target = impactEnergy;
const type = 'Impact Year: ' + impactYear + ', Latitude: ' + latitude + ', Longitude: ' + longitude;
return {
sorce: (impactEnergy < 0.15 ? "0-0.15" : (impactEnergy < 0.25 ? "0.15-0.25" : (impactEnergy < 0.5 ? "0.25-0.5" : ">0.5"))),
target,
type
}
});
data.push(showData);
return data;
`

Related

How to access the BufferGeometry of IFC items in web-ifc-three

I'm trying to get the geometry of an element
i.e. a BufferGeometry object corresponding to an expressId I have (not through picking).
Basically I'm asking how to traverse the IFC model and export each object as a separate OBJ.
I'll note I have reverse engineered code to achieve that for some version of the package, but it uses undocumented functionality, so naturally it broke in later versions (the code also colors the geometry according to the material's color so I don't need an mtl):
Don't copy this code it won't work
Object.values(bimModel.ifcManager.state.models[bimModel.modelID].items).forEach(type => {
Object.entries(type.geometries).forEach(([id, geometry]) => {
const properties = bimModel.getItemProperties(Number(id))
const numVertices = geometry.getAttribute('position').count
const color = type.material.color.toArray().map(x => x * 255)
const vertexColors = new Uint8Array(Array.from({ length: numVertices }, () => color).flat())
geometry.setAttribute('color', new BufferAttribute(vertexColors, 3, true))
})
})
This is exactly what we do to export models to glTF. The basic workflow is:
Decide what IFC categories you would like to export.
Get all the items of each category.
Reconstruct the mesh for each item.
Export the mesh using the Three.js exporter of your choice.
Let's see a basic example to get all the meshes from the walls. The process is not as straightforward as having each IFC item as a separate mesh, but that's the price for having the draw calls at minimum (otherwise, a browser wouldn't stand even medium-sized IFC files):
import { IFCWALLSTANDARDCASE } from 'web-ifc';
async function getAllWallMeshes() {
// Get all the IDs of the walls
const wallsIDs = manager.getAllItemsOfType(0, IFCWALL, false);
const meshes = [];
const customID = 'temp-gltf-subset';
for (const wallID of wallsIDs) {
const coordinates = [];
const expressIDs = [];
const newIndices = [];
const alreadySaved = new Map();
// Get the subset for the wall
const subset = viewer.IFC.loader.ifcManager.createSubset({
ids: [wallID],
modelID,
removePrevious: true,
customID
});
// Subsets have their own index, but share the BufferAttributes
// with the original geometry, so we need to rebuild a new
// geometry with this index
const positionAttr = subset.geometry.attributes.position;
const expressIDAttr = subset.geometry.attributes.expressID;
const newGroups = subset.geometry.groups
.filter((group) => group.count !== 0);
const newMaterials = [];
const prevMaterials = subset.material;
let newMaterialIndex = 0;
newGroups.forEach((group) => {
newMaterials.push(prevMaterials[group.materialIndex]);
group.materialIndex = newMaterialIndex++;
});
let newIndex = 0;
for (let i = 0; i < subset.geometry.index.count; i++) {
const index = subset.geometry.index.array[i];
if (!alreadySaved.has(index)) {
coordinates.push(positionAttr.array[3 * index]);
coordinates.push(positionAttr.array[3 * index + 1]);
coordinates.push(positionAttr.array[3 * index + 2]);
expressIDs.push(expressIDAttr.getX(index));
alreadySaved.set(index, newIndex++);
}
const saved = alreadySaved.get(index);
newIndices.push(saved);
}
const geometryToExport = new BufferGeometry();
const newVerticesAttr = new BufferAttribute(Float32Array.from(coordinates), 3);
const newExpressIDAttr = new BufferAttribute(Uint32Array.from(expressIDs), 1);
geometryToExport.setAttribute('position', newVerticesAttr);
geometryToExport.setAttribute('expressID', newExpressIDAttr);
geometryToExport.setIndex(newIndices);
geometryToExport.groups = newGroups;
geometryToExport.computeVertexNormals();
const mesh = new Mesh(geometryToExport, newMaterials);
meshes.push(mesh);
}
viewer.IFC.loader.ifcManager.removeSubset(modelID, undefined, customID);
return meshes;
}

Optimising Google Speed Insights API Script in Google Sheets

I have created a script in Google Apps Scripts and Google Sheets that returns some speed metrics from the urls that are pasted in the sheet.
The script works good, the only problem is that it takes forever to present the results in the sheet. It makes a call for each url, I suspect that's why it's slow.
Is there any way I can optimise this script so it gives me the results faster?
Screenshot
The code:
const sheet = SpreadsheetApp.getActiveSpreadsheet();
const API_STRING = sheet.getSheetByName("instructions").getRange("K10").getValues();
const PLATFORM = sheet.getSheetByName("urls").getRange("B1").getValues();
const OUTPUT_CELL = sheet.getSheetByName("urls").getRange("B5:" + ("K" + sheet.getLastRow()));
console.log(PLATFORM);
// KPI
const lighthouseMetrics = [
"first-contentful-paint",
"largest-contentful-paint",
'interactive',
"cumulative-layout-shift",
"speed-index",
"total-blocking-time"
]
const fieldData = [
"FIRST_CONTENTFUL_PAINT_MS",
"LARGEST_CONTENTFUL_PAINT_MS",
"FIRST_INPUT_DELAY_MS",
"CUMULATIVE_LAYOUT_SHIFT_SCORE"
]
// CALLING FUNCTION
async function fetch_array() {
let URLS_LIST = sheet.getSheetByName("urls").getRange("A5:" + ("A" + sheet.getLastRow())).getValues();
console.log(URLS_LIST)
let arrayData = [];
for (let element of URLS_LIST) {
let dataEl = await getPageSpeedInfo(PLATFORM, element);
let dataRow = produceArray(dataEl);
arrayData.push(dataRow);
}
return OUTPUT_CELL.setValues(arrayData);
}
// PRODUCE ARRAY WITH KPIS
function produceArray(data) {
let kpiArray = [];
fieldData.forEach(function(item) {
let fieldDataRoute = data.loadingExperience.metrics[item].category;
kpiArray.push(fieldDataRoute);
})
lighthouseMetrics.forEach(function(item) {
let lighthouseRoute = data.lighthouseResult.audits[item].displayValue;
kpiArray.push(lighthouseRoute);
})
return kpiArray;
}
// CALL TO API
async function getPageSpeedInfo(strategy, element) {
let pageSpeedUrl = 'https://www.googleapis.com/pagespeedonline/v5/runPagespeed?url=' + element + '&key=' + API_STRING + '&strategy=' + strategy;
console.log(pageSpeedUrl);
let response = await UrlFetchApp.fetch(pageSpeedUrl);
let data = await response.getContentText();
return JSON.parse(data);
}

Is there a way to use fusion algorithms like STARFM in Google Earth Engine?

Landsat and MODIS products both have their advantages. Landsat with its high spatial resolution and MODIS with its high temporal resolution. I've read a lot about downloading the files and fuse them locally with algorithms like STARFM in Python for example. Is there a way to fuse both collections directly in Google Earth Engine to save computational time?
There is Google earth engine code in GitHub. The link is below:
https://github.com/MacGallagher/GEE_STARFM_FUSION
The code I copied is below:
/**** Start of imports. If edited, may not auto-convert in the playground. ****/
var l8 = ee.ImageCollection("LANDSAT/LC08/C01/T1_SR"),
modis = ee.ImageCollection("MODIS/006/MOD09GQ"),
geometry4 = /* color: #d63000 */ee.Geometry.Polygon(
[[[-116.004638671875, 42.809506838324204],
[-115.59539794921875, 42.78129125156276],
[-115.499267578125, 43.004647127794435],
[-115.499267578125, 43.28920196020127],
[-116.00189208984375, 43.35114690203121],
[-116.19415283203125, 43.07691312608711]]]),
geometry = /* color: #98ff00 */ee.Geometry.Polygon(
[[[-115.93391418457031, 43.09346209534859],
[-115.80276489257812, 43.095718426590174],
[-115.80276489257812, 43.161366298103566],
[-115.9332275390625, 43.16086543618915]]]);
/***** End of imports. If edited, may not auto-convert in the playground. *****/
// based on script by Faye Peters, University of Louisville
// modified by Megan Gallagher, Boise State University
// for code inquiries, or adjustments please email megangallagher#u.boisestate.edu
// code exists for:
// landsat 5,7, and 8 NDVI merging with MODIS Terra daily
// MODIS Terra and Sentinel-2
// Landsat pre tier and modis daily terra - works with base R code
// Landsat 8 and Sentinel-2
////////////////////////////////////////Variables//////////////////////////////////////////////
// import Landsat 8 and MODIS daily terra surface reflectance 250m
var region = geometry; //region you want to export
var bounds = geometry4 //outer bounds of image to catch boundary effect, make larger than region
var date_begin = '2016-03-02' //start date of data collection, must be a landsat image date
var date_end ='2016-10-13' // end date of data collection, must be the day AFTER last landsat image
var csv_title = '2016_Dates_BOP' //title of csv output
var ls_title = '2016_landsat' // title of landsat tif file
var mod_title = '2016_mod' // title of modis tif file
/////////////////////////////////////////Code////////////////////////////////////////////////////////////////////////
var starfm = function(modis, l8){
//Preliminary filtering of MODIS and Landsat
var filt_mod = modis.filterDate(date_begin, date_end);
var filt_l8 = l8.filterDate(date_begin, date_end)
.filterBounds(bounds);
//add bounds to subset area
var subset_bounds = bounds.transform('EPSG:4326', 30).bounds().getInfo();
// get rid of bad pixels but keep metadata date information
var removeBadObservations = function(image){
var valid_data_mask = ee.Image(image).select('pixel_qa').bitwiseAnd(2).neq(0);
var numberBandsHaveData =
image.mask().reduce(ee.Reducer.sum());
var allOrNoBandsHaveData =
numberBandsHaveData.eq(0).or(numberBandsHaveData.gte(9));
var allBandsHaveData = allOrNoBandsHaveData;
//Make sure no band is just under zero
var allBandsGT = image.reduce(ee.Reducer.min()).gt(-0.001);
var result = ee.Image(image).mask(image.mask().and(valid_data_mask).and(allBandsHaveData).and(allBandsGT));
return result.copyProperties(ee.Image(image),['system:time_start']);
};
//NDVI functions for Landsat and MODIS
var getNDVI_mod = function(image){
return image
.addBands(image.normalizedDifference(['sur_refl_b02','sur_refl_b01']).multiply(10000).rename('NDVI'));};
var getNDVI_l8= function(image){
var ndvi = ee.Image(image).normalizedDifference(['B5','B4']);
return ndvi.copyProperties(ee.Image(image),['system:time_start']);
};
var filt2_l8= filt_l8.filterBounds(subset_bounds).aside(print).map(removeBadObservations).map(getNDVI_l8);
// update mask for different areas
var subset_mask = ee.Image().byte().paint(geometry4, "id").add(1);
var filtered_modis = filt_mod.filterBounds(subset_bounds).aside(print).map(getNDVI_mod);
// Pull out the date:
var extract_modis_date = function(row) {
var d = ee.Date(row.get('system:time_start'));
var d2 = ee.Date.fromYMD(d.get('year'), d.get('month'), d.get('day'));
var result = ee.Feature(null, {'date': d2});
result = result.set({'date': d2});
return result;
};
var getQABits = function(image, start, end, newName) {
//// Compute the bits we need to extract.
var pattern = 0;
for (var i = start; i <= end; i++) {
pattern += Math.pow(2, i);
}
//// Return a single band image of the extracted QA bits, giving the band a new name.
return image.select([0], [newName])
.bitwiseAnd(pattern)
.rightShift(start); };
print(filtered_modis);
filtered_modis = filtered_modis.map(function(image){
var quality = getQABits(image.select(2), 4, 5, 'QAMask');
quality = quality.eq(3).not();
return image.clip(subset_bounds).mask(image.mask().multiply(subset_mask).multiply(quality));
});
filtered_modis = filtered_modis.select('NDVI'); //Take only NDVI band
var modis_multiband = ee.Image(filtered_modis.filterDate(date_begin, date_end).iterate( function(x, modis_multiband) {
return ee.Image(modis_multiband).addBands(ee.Image(x));
}, filtered_modis.first()));
var dates_modis = filtered_modis.map(extract_modis_date);
print(dates_modis.getInfo());
var filt2_l8_ndvi = filt2_l8.map(function(image) {
return ee.Image(image)
.clip(subset_bounds)
.mask(ee.Image(image).mask().multiply(subset_mask));
});
//use this to choose the range of days, and check for overlap
var day_expand = 1;
var reduceLandsatNDVI = function(MODISdate) {
MODISdate = ee.Date(MODISdate.get('date'));
var ndvi_subset = ee.ImageCollection(filt2_l8_ndvi).filterDate( MODISdate,
MODISdate.advance(day_expand, 'day') );
ndvi_subset = ndvi_subset.map(function (image) {
var diff = MODISdate.difference(ee.Date(ee.Image(image).get('system:time_start')), 'day').abs();
return ee.Image(image).set('diff', diff);
});
ndvi_subset = ndvi_subset.sort('diff');
var ndvi_first = ndvi_subset.reduce('first');
var ndvi_mean = ndvi_subset.reduce('mean');
return ee.Algorithms.If(
ndvi_first.bandNames(),
ndvi_first.eq(0).multiply(ndvi_mean).add(ndvi_first),
ee.Image(0)
);
};
var extract_landsat_date = function(MODISdate) {
MODISdate = ee.Date(MODISdate.get('date'));
var ndvi_subset =
ee.ImageCollection(filt2_l8_ndvi).filterDate( MODISdate,
MODISdate.advance(day_expand, 'day') );
ndvi_subset = ndvi_subset.map(function (image) {
var diff = MODISdate.difference(ee.Date(ee.Image(image).get('system:time_start')), 'day').abs();
return ee.Image(image).set('diff', diff);
});
ndvi_subset = ndvi_subset.sort('diff');
var d = ndvi_subset.aggregate_first('system:time_start');
var count = ndvi_subset.aggregate_count('system:time_start');
d = ee.Algorithms.If(
ee.Number(count).gt(0),
ee.Date(d),
ee.Date('1971-01-01')
);
d = ee.Date(d);
var d2 = ee.Date.fromYMD(d.get('year'), d.get('month'),
d.get('day'));
var result = ee.Feature(null, {'LSdate': d2, 'MODISdate':
MODISdate, 'CountLSScenes': count});
result = result.set({'LSdate': d2, 'MODISdate': MODISdate,
'CountLSScenes': count});
return result;
};
var ls_collection = dates_modis.map(reduceLandsatNDVI);
print(ls_collection,'ls collection');
var dates_landsat = dates_modis.map(extract_landsat_date);
Export.table(dates_landsat, csv_title);
var ls_multiband = ls_collection.iterate( function(x,
ls_multiband)
{ return ee.Image(ls_multiband).addBands(ee.Image(x));
}, ls_collection.first());
ls_multiband = ee.Image(ls_multiband).multiply(10000).int16();
ls_multiband = ls_multiband.mask(ls_multiband.mask().multiply(ls_multiband.neq(0)));
//remove repeated first layer
var ls_multiband2=ee.Image(ls_multiband.slice(1))
var modis_multiband2=ee.Image(modis_multiband.slice(1))
Export.image.toDrive({
image: modis_multiband2,
description: mod_title,
crs:'EPSG:4326 ',
region:region,
scale:30
});
Export.image.toDrive({
image: ls_multiband2,
description: ls_title,
crs:'EPSG:4326',
region:region,
scale:30
});
return 'Done!'
}
var running = starfm(modis,l8)
print(running)
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////

ThreeJS: Is there any possible way to reduce the amount of triangles count

Hi I have some very complex models to display which comes from Revit files that my customer provides.
But sometimes the amount of details in the model is just way too much for the purpose of the website.
I would like to reduce the amount of vertexes/triangles in the model to simplify the display and enhance performance.
I have used simply modifiers where I cant able to view the model itself.
Is this possible from within ThreeJS? Or is there maybe an other solution for this?
const renderMeshFunction = async (material: any, geometry: any, id: any) => {
let count = 0;
const emptyFunction = () => { };
const onBR = () => {
const _count = (count < 3 ? count : Math.floor(Math.random() * count) + 1);
return ((renderer: WebGLRenderer, scene: Scene,
camera: Camera, geometry: Geometry | BufferGeometry,
_material: Material, group: Group) => {
_material.clippingPlanes = checkIsClipping();
});
};
count = geometry.maxInstancedCount;
const mesh = new Mesh(geometry, material);
let modifier = new SimplifyModifier();
let simplified = mesh.clone();
simplified.material = simplified.material.clone();
let countSimple = Math.floor((simplified.geometry as any).attributes.position.count * 1); // number of vertices to remove
simplified.geometry = modifier.modify(simplified.geometry, countSimple);
that.scene.add(simplified);
mesh.name = id || Date.now().toString();
mesh.matrixAutoUpdate = false;
mesh.drawMode = 0;
mesh.onBeforeRender = onBR.apply(that);
mesh.onAfterRender = emptyFn;
mesh.geometry.dispose();
mesh.material.dispose();
geometry.dispose();
material.dispose();
material = undefined;
return mesh;
};

Create map after get information of location through AJAX

I need to get the longitude and latitude of a location from a text file through AJAX then create a map using that information. This is what I have done:
function createMap(){
var request;
if (window.XMLHttpRequest)
{
request = new XMLHttpRequest();
}
if (request)
{
var number = Math.floor( (Math.random() * 3) + 1 );
var url = "text_" + number + ".txt";
request.open("GET", url,true);
request.onreadystatechange = function()
{
if (request.readyState == 4 && request.status == 200)
{
var syd=new google.maps.LatLng(-33.884183, 151.214944);
var woll=new google.maps.LatLng(request.responseText);
function initialize()
{
var mapProp = {
center:syd,
zoom:6,
mapTypeId:google.maps.MapTypeId.ROADMAP
};
var map=new google.maps.Map(document.getElementById("outputAJAX"),mapProp);
var myTrip=[syd,woll];
var flightPath=new google.maps.Polyline({
path:myTrip,
strokeColor:"#0000FF",
strokeOpacity:0.8,
strokeWeight:2
});
flightPath.setMap(map);
}
initialize();
}
}
request.send();
} else {
document.getElementById("outputAJAX").innerHTML = "<span style='font-weight:bold;'>AJAX</span> is not supported by this browser!";
}}
However, the map didn't show up. Do you guys have any suggestion?
A google.maps.LatLng object takes two Numbers as its arguments:
LatLng(lat:number, lng:number, noWrap?:boolean) Creates a LatLng object representing a geographic point. Latitude is specified in degrees within the range [-90, 90]. Longitude is specified in degrees within the range [-180, 180]. Set noWrap to true to enable values outside of this range. Note the ordering of latitude and longitude.
This won't work (you are only passing one argument, a string):
var woll=new google.maps.LatLng(request.responseText);
Assuming request.responseText is a comma separated string containing two numeric values, this should work:
var coordStr = request.responseText;
var coords = coordStr.split(",");
var woll = new google.maps.LatLng(parseFloat(coords[0]),
parseFloat(coords[1]));
proof of concept fiddle

Resources