I've been given the script below on Google Earth Engine to extract data along a transect. (https://code.earthengine.google.com/e31179d9e7143235092d6b4fa29a12fd) In the GEE code editor the top of the scipt has an import flag (picture attached).
Multiple references to 'line' are made, which I understand to be a variable that has been declared, but I can't find it. I've looked in the GEE documentation, and in a JavaScript reference to determine if it's a method or some such like but I can't work it out.
The imported data is declared as 'transect', so it's not that.
/***
* Reduces image values along the given line string geometry using given reducer.
*
* Samples image values using image native scale, or opt_scale
*/
function reduceImageProfile(image, line, reducer, scale, crs) {
var length = line.length();
var distances = ee.List.sequence(0, length, scale)
var lines = line.cutLines(distances, ee.Number(scale).divide(5)).geometries();
lines = lines.zip(distances).map(function(l) {
l = ee.List(l)
var geom = ee.Geometry(l.get(0))
var distance = ee.Number(l.get(1))
geom = ee.Geometry.LineString(geom.coordinates())
return ee.Feature(geom, {distance: distance})
})
lines = ee.FeatureCollection(lines)
// reduce image for every segment
var values = image.reduceRegions( {
collection: ee.FeatureCollection(lines),
reducer: reducer,
scale: scale,
crs: crs
})
return values
}
// Define a line across the Olympic Peninsula, USA.
// Import a digital surface model and add latitude and longitude bands.
var elevImg = ee.Image('JAXA/ALOS/AW3D30/V2_2').select('AVE_DSM');
var profile = reduceImageProfile(elevImg, transect, ee.Reducer.mean(), 100)
print(ui.Chart.feature.byFeature(profile, 'distance', ['mean']))
line isn't a variable, it's a parameter. Parameters are very similar to local variables within the function, but instead of being declared with var, let, or const, they're declared in the function's parameter list:
function reduceImageProfile(image, line, reducer, scale, crs) {
// here −−−−−−−−−−−−−−−−−−−−−−−−−−−^
The parameter's value is filled in each time the function is called using the corresponding argument in the function call. Let's take a simpler example:
function example(a, b) {
// ^−−^−−−−−−−−−− parameter declarations
return a + b;
}
// vv−−−−−−−−− argument for `a`
console.log(example(40, 2));
// ^−−−−−− argument for `b`
// vv−−−−−−−−− argument for `a`
console.log(example(60, 7));
// ^−−−−−− argument for `b`
In the first call to example, the a parameter receives the value 40 and the b parameter receives the value 2 from the call arguments. In the second call, the a parameter receives the value 60 and the b parameter receives the value 7 from the call arguments.
Related
Okay so I've been trying to map some heatmaps to a Revit room using the DataViz api. I was able to get X Y Z from Revit for the sensor inside the rooms, i've substracted the viewer.model.getGlobalOffset() and managed to show some sprites on these points. I know for a fact that those sprites / points are inside Rooms, but whenever I try to use the same points to load a heatmap I get the Some devices did not map to a room: warning and no heatmap is displayed.
Following the API documentation this warning appears when there is no room information in the point. Did I miss anything? This is "my" code:
async function loadHeatmaps(model){
const dataVizExtn = await viewer.loadExtension("Autodesk.DataVisualization");
// Given a model loaded from Forge
const structureInfo = new Autodesk.DataVisualization.Core.ModelStructureInfo(model);
const devices = [
{
id: "Oficina 6", // An ID to identify this device
name:"Oficina-",
position: { x: 22.475382737884104, y: 7.4884431474006163, z: 3.0 }, // World coordinates of this device
sensorTypes: ["temperature", "humidity"], // The types/properties this device exposes
}
];
var offset = viewer.model.getGlobalOffset();
removeOffset(devices[0],offset)
// Generates `SurfaceShadingData` after assigning each device to a room.
const shadingData = await structureInfo.generateSurfaceShadingData(devices);
console.log(shadingData)
// Use the resulting shading data to generate heatmap from.
await dataVizExtn.setupSurfaceShading(model, shadingData);
// Register color stops for the heatmap. Along with the normalized sensor value
// in the range of [0.0, 1.0], `renderSurfaceShading` will interpolate the final
// heatmap color based on these specified colors.
const sensorColors = [0x0000ff, 0x00ff00, 0xffff00, 0xff0000];
// Set heatmap colors for temperature
const sensorType = "temperature";
dataVizExtn.registerSurfaceShadingColors(sensorType, sensorColors);
// Function that provides sensor value in the range of [0.0, 1.0]
function getSensorValue(surfaceShadingPoint, sensorType) {
// The `SurfaceShadingPoint.id` property matches one of the identifiers passed
// to `generateSurfaceShadingData` function. In our case above, this will either
// be "cafeteria-entrace-01" or "cafeteria-exit-01".
const deviceId = surfaceShadingPoint.id;
// Read the sensor data, along with its possible value range
let sensorValue = readSensorValue(deviceId, sensorType);
const maxSensorValue = getMaxSensorValue(sensorType);
const minSensorValue = getMinSensorValue(sensorType);
// Normalize sensor value to [0, 1.0]
sensorValue = (sensorValue - minSensorValue) / (maxSensorValue - minSensorValue);
return clamp(sensorValue, 0.0, 1.0);
}
// This value can also be a room instead of a floor
const floorName = "01 - Entry Level";
dataVizExtn.renderSurfaceShading(floorName, sensorType, getSensorValue);
}
function removeOffset(pos, offset) {
pos.position.x = pos.position.x - offset.x;
pos.position.y = pos.position.y - offset.y;
pos.position.z = pos.position.z - offset.z;
}
And I'm calling the loadHeatmaps() function inside onDocumentLoadSuccess callback.
EDIT: It looks like in this particular case it was a problem with floorName not being set to the right value. Note that this value (first parameter to dataVizExtn.renderSurfaceShading) should be set either to the name of the room, or to the name of the floor you want to update.
The offsets are a bit tricky so I'd suggest debugging that area, for example:
What coordinate system are the sensors defined in? If they are in the same coordinate system as the building model itself, you shouldn't subtract or add any offset to them. Whenever there's a model with a "global offset" in its metadata, it basically means that the Model Derivative service moved the model to origin to avoid floating point precision issues, and the viewer will then add the global offset back to each geometry when its loaded.
Try using the viewer APIs to get the bounding box of one of the rooms that the devices should map to, and see if the bounding box actually contains the XYZ point of the device you're trying to pass into the DataViz extension. The bounds of any object can be found like so:
function getObjectBounds(model, dbid) {
const tree = model.getInstanceTree();
const frags = model.getFragmentList();
let bounds = new THREE.Box3();
tree.enumNodeFragments(dbid, function (fragid) {
let _bounds = new THREE.Box3();
frags.getWorldBounds(fragid, _bounds);
bounds.union(_bounds);
}, true);
return bounds;
}
I have the same issue and my revit model was built by revit 2020. When I update model to 2022, heatmap can show on the room correctly.
I am trying to draw an arrowhead for each point in a kml file. For this I plan to fetch coordinates for each point by getById. So far I am getting an error:
Uncaught TypeError: Cannot read property 'position' of undefined (on line 14)
Here is my code:
var src = Cesium.KmlDataSource.load('../../My_KML/plots.kml', options);
viewer.dataSources.add(src).then(function(data) {viewer.flyTo(data);});
//-------------------********--------------**********-----------------//
var point = viewer.entities.getById('geom_20102');
var entities = viewer.entities;
var cartographicPosition = Cesium.Cartographic.fromCartesian(point.position.getValue(Cesium.JulianDate.now()));
var latitude = Cesium.Math.toDegrees(cartographicPosition.latitude);
var longitude = Cesium.Math.toDegrees(cartographicPosition.longitude);
var line1 = entities.add({
polyline : {
positions : Cesium.Cartesian3.fromDegreesArrayHeights([longitude, latitude, 360, longitude + 1, latitude + 1, 400]),
width : 10,
followSurface : false,
material : new Cesium.PolylineArrowMaterialProperty(Cesium.Color.BLUE)
}
});
I have specified the element with id 'geom_20102' as a linestring wrapped around by a placemark in the kml. Also I would like to know which id to specify as both placemark and linestring have an id. Or am I confusing kml id with entity id?
I am new to Cesium.Js and I followed this example partially:
Cesium Workshop
KML snippet:
<Placemark id="feat_20125">
<name>874</name>
<styleUrl>#stylesel_20102</styleUrl>
<LineString id="geom_20102">
<coordinates>104.99108,10.4118,247.3 72.991075,26.25412,247.6</coordinates>
<altitudeMode>relativeToGround</altitudeMode>
</LineString>
</Placemark>
Two things are going on here.
First, the Cesium.KmlDataSource.load() function returns a JavaScript "Promise" which represents the eventual completion (or failure) of an asynchronous operation. At the time the viewer.entities is referenced in the code, the KML file has not yet loaded so the collection in viewer.entities is empty and calling getById() on it will return undefined. You should only access the viewer.entities or data.entities after the async Promise is completed and the "then" callback is invoked. Only at that time are the entities populated.
var src = Cesium.KmlDataSource.load('../../My_KML/plots.kml', options);
viewer.dataSources.add(src).then(function(data) {
var entities = data.entities;
console.log("f=" + entities.getById('feat_20125')); // f=[object Object]
console.log("g=" + entities.getById('geom_20102')); // undefined
viewer.flyTo(data);
});
Next, notice that the 'feat_20125' returns an object but the 'geom_20102' is not found. Only the "id" on the placemarks are populated when KML is converted into Cesium entities. Ids on any other KML element are discarded.
I am creating a vector object which as the following function:
Vector.prototype.limitTo = function (pScalar) {
this.normalise();
this.multiply(pScalar);
if (this.magnitude() > pScalar) {
this.magnitude = 30;
}
return new Vector(this.getX(), this.getY(), this.getZ());
};
In this I am trying to make it comply with the this spec:
"your Vector object should have a ‘limitTo’ function that takes a single scalar number as its parameter. The function should return a newly constructed Vector object that has the same direction as the ‘this’ Vector, but if its magnitude exceeds the given parameter value then it is reduced in size to equal the maximum value. The direction of the Vector should be unaffected, only the magnitude may be altered. If the magnitude of the Vector does not exceed the maximum value, then it should not be altered."
And a jasmine test of:
describe("Limit To", function () {
var limitedVector, magnitude;
it("Magnitude not exceeding limit", function () {
limitedVector = vector.limitTo(60);
magnitude = limitedVector.magnitude();
expect(magnitude).toEqual(50);
});
it("Magnitude exceeding limit", function () {
limitedVector = vector.limitTo(30);
magnitude = limitedVector.magnitude();
expect(magnitude).toEqual(30);
});
});
I have the magnitude not exceeding limit but, am having trouble getting the exceeding limit test.
You didn't include your methods, but assigning a number to a method property seems wrong. Most probably you want
Vector.prototype.limitTo = function (pScalar) {
return this.normalise().multiply(Math.min(his.magnitude(), pScalar));
};
If this.normalise() is in-place, copy your vector first.
i am trying to update a line graph and it is not throwing any error but it is also not updating the graph.
i am deleting a point and adding a new one with an incremented rate and incremented created_at date by a second(trying to follow http://bl.ocks.org/benjchristensen/1148374)
function redrawWithoutAnimation() {
for (var i in chart_data) {
linedata = chart_data[i];
//delete first element of array
linedata.points.reverse().shift();
//create a new point
rate = linedata.points[0].rate + 1;
created_at = linedata.points[0].created_at + 6000;
new_point = {};
new_point.rate = rate;
new_point.created_at = created_at;
linedata.points.push(new_point);
console.log(linedata);
}
// static update without animation
svg.selectAll("path")
.data([linedata.points]); // set the new data
line(linedata.points); // apply the new data values
}
redrawWithoutAnimation();
setInterval(function () {
redrawWithoutAnimation();
}, 8000);
here is my code
http://jsfiddle.net/yr2Nw/8/
Working fiddle: http://jsfiddle.net/reblace/GsaGb/1
There's a few issues here...
First, you were updating all the chart_data in the for loop, but outside the loop, you were only trying to update the line still stored in the linedata variable after loop execution. You should try to avoid having variables with greater scope than they need. It can lead to bugs like this one:
svg.selectAll("path").data([linedata.points]);
line(linedata.points);
You should instead use D3's data joining to rejoin the new data to all the paths at once declaratively like so:
linesGroup.selectAll("path")
.data(chart_data)
.attr("d", function(d){ return line(d.points); });
What that code's doing is it's selecting the paths and then joining each of them to the chart_data elements and then binding the appropriate line generator to the "d" attribute for the appropriate path.
Then, you need to update your x axis and y axis otherwise the plot will just shoot off the drawn area. This code is updating the domains and then rebinding the axes to the dom elements so they redraw:
xAxis.scale().domain([
d3.min(chart_data, function (c) { return d3.min(c.points, function (v) { return v.created_at; }); }),
d3.max(chart_data, function (c) { return d3.max(c.points, function (v) { return v.created_at; }); })
]);
yAxis.scale().domain([
0,
d3.max(chart_data, function (c) { return d3.max(c.points, function (v) { return v.rate; }); })
]);
svg.select(".x.axis").call(xAxis);
svg.select(".y.axis").call(yAxis);
There were a few other bugs I fixed them in the Fiddle. For example, you need to calculate the time for the new point based on the last element in the array, not the first, otherwise the line can't interpolate properly since its no longer a continuous function... and this is a bit more concise way to do your line updates:
for (var i=0; i<chart_data.length; i++) {
linedata = chart_data[i];
//delete first element of array
var removedPoint = linedata.points.shift();
//create a new point
var lastpoint = linedata.points[linedata.points.length-1];
var new_point = {
rate: removedPoint.rate,
created_at: lastpoint.created_at + 6000
};
linedata.points.push(new_point);
}
Also note that you shouldn't use the for(var in) loop for Arrays, that's for iterating over the properties in an object.
There's still some issues, but I think this should help get you over the hurdle you were stuck on. Anyways, it looks cool in action!
Fine fenac.. You facing so many problems since your data is not in good format for your requirements..
as per http://bl.ocks.org/benjchristensen/1148374 The x-axis data must be (data[] (data array))
Your data is something like this
[objects,object,object] where each object holds one element of xaxis value.. so the pushing and shifting is not possible..
try to change the format of the data (linedata.points) to an array (data[]) and try it out sure it works..
You just need to put all the values in linedata.points into an array data[] and use this data[] to animate your line..
Since yours the multiline.. you need to create 2D array and must pass them accordingly...
Cheers..
I updated your jsfiddle
setInterval(function () {
console.log(linedata.points);
var v = linedata.points.shift(); // remove the first element of the array
linedata.points.push(v); // add a new element to the array (we're just taking the number we just shifted off the front and appending to the end)
redrawWithoutAnimation();
}, 3000);
http://jsfiddle.net/yr2Nw/9/
But still it wont works till you do that work...
Personal Suggestion: First Try with single line graph then go with looping for multiline...
Here is my confusion(jsfiddle-demo) about the category10 function in D3:
> var a = d3.scale.category10()
> a(0)
"#1f77b4"
> a(10) //the expected different color value
"#2ca02c"
If I call directly the returned function of calling category10 , things go like this
> d3.scale.category10()(0)
"#1f77b4"
> d3.scale.category10()(10) //the SAME color! Why?
"#1f77b4"
In my opinion, calling d3.scale.category10()(10) should yield the same value as calling a(10).
What is going wrong here?
Each call to d3.scale.category10() returns a new ordinal scale instance, so by calling it like d3.scale.category10()(10) you are using a new instance each time. Each ordinal scale instance can either be explicitly configured with an input domain (mapping input values to output colors), or it can do so implicitly, where it just returns the first color for the first input value, and so on, creating the mapping as you use it.
In your example you're using a new instance with each call, so no matter what value you input, you will get the first color back. Even your earlier examples might lead to some unexpected behavior unless you explicitly configure the input domain. For example:
var a = d3.scale.category10()
a(0) // => "#1f77b4"
a(10) // => "#ff7f0e"
var b = d3.scale.category10()
b(10) // => "#1f77b4"
b(0) // => "#ff7f0e"
Here's how you can set the input domain to always return the Nth color whenever you input N no matter what order you make the calls:
var a = d3.scale.category10().domain(d3.range(0,10));
a(0) // => "#1f77b4"
a(1) // => "#ff7f0e"
a(2) // => "#2ca02c"
var b = d3.scale.category10().domain(d3.range(0,10));
b(2) // => "#2ca02c"
b(1) // => "#ff7f0e"
b(0) // => "#1f77b4"
BTW, as an aside, even now a(10) returns the same as a(0) but that's because 10 is outside the range [0,10], which starts at 0 and ends at 9, so a(10) is an unassigned input and gets the next color, which happens to be the first.