How to clip a cloud mask function in google earth engine? - javascript
I'm looking to clip the cloud mask function (top of my code) to the AOI. For some reason, my other layers (dNDVI and RGB) work for the AOI, but my cloud mask doesn't apply to it. I've tried putting in the ee.Image.clip function but I couldn't get it to work.
I would love to have some insight as to why it won't apply for this specific layer. Very new to coding!
// --------- Cloud mask: -----------------
var mask_all = function(image) {
var hollstein_mask = cld.hollstein_S2(['shadow', 'cloud', 'cirrus'])(image);
return hollstein_mask;
};
var color = "#98ff00";
var AOI = ee.Geometry.Polygon(
[[[140.88237590701584,-37.896469860299604],
[140.96546001346115,-37.896469860299604],
[140.96546001346115,-37.83819826191272],
[140.88237590701584,-37.83819826191272],
[140.88237590701584,-37.896469860299604]]], null, false);
/* [[[140.5710269570096,-37.669974265519755],
[140.64037815329866,-37.669974265519755],
[140.64037815329866,-37.60037237657578],
[140.5710269570096,-37.60037237657578],
[140.5710269570096,-37.669974265519755] */
//load images for composite
var Sen2mosaic = ee.ImageCollection("COPERNICUS/S2_SR");
ee.Geometry.Polygon(140.88237590701584,-37.896469860299604);
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);
};
// A function to mask out cloudy pixels.
var cloud_shadows = function(image) {
// Select the QA band.
var QA = image.select(['B8', 'B11', 'B4']);
// Get the internal_cloud_algorithm_flag bit.
return getQABits(QA, 3,3, 'cloud_shadows').eq(0);
// Return an image masking out cloudy areas.
};
// A function to mask out cloudy pixels.
var clouds = function(image) {
// Select the QA band.
var QA = image.select(['B8', 'B11', 'B4']);
// Get the internal_cloud_algorithm_flag bit.
return getQABits(QA, 5,5, 'Cloud').eq(0);
// Return an image masking out cloudy areas.
};
var maskClouds = function(image) {
var cs = cloud_shadows(image);
var c = clouds(image);
image = image.updateMask(cs);
return image.updateMask(c);
};
// --------- Input: ------------------------------------
var startDate = ee.Date('2018-12-31') //2016-09-15');
var endDate = ee.Date('2021-06-01');
var AOI =
ee.Geometry.Polygon(
[[[140.88237590701584,-37.896469860299604],
[140.96546001346115,-37.896469860299604],
[140.96546001346115,-37.83819826191272],
[140.88237590701584,-37.83819826191272],
[140.88237590701584,-37.896469860299604]]], null, false);
/* [[[140.5710269570096,-37.669974265519755],
[140.64037815329866,-37.669974265519755],
[140.64037815329866,-37.60037237657578],
[140.5710269570096,-37.60037237657578],
[140.5710269570096,-37.669974265519755] */
//Map.addLayer(AOI, {}, 'AOI', true);
Map.centerObject(AOI, 13);
var imageStartDate1 = startDate.advance(-30,"day");
var imageStartDate2 = startDate.advance(30,"day");
var imageEndDate1 = endDate.advance(-30,"day");
var imageEndDate2 = endDate.advance(30,"day");
var imagery = ee.ImageCollection("COPERNICUS/S2_SR");
//S2-SR: COPERNICUS/S2_SR //LANDSAT/LC08/C01/T1_SR
var Sen2 = ee.ImageCollection(imagery
// Filter by dates.
.filterDate(imageStartDate1, imageStartDate2)
// Filter by location.
.filterBounds(AOI));
var Sen2end = ee.ImageCollection(imagery
// Filter by dates.
.filterDate(imageEndDate1, imageEndDate2)
// Filter by location.
.filterBounds(AOI));
var Sen2mosaic = Sen2.mosaic().clip(AOI);
var Sen2mosaicEnd = Sen2end.mosaic().clip(AOI);
//print("Sen2mosaic", Sen2mosaic);
var composite_free = Sen2end.map(maskClouds);
var visParams = {bands:['B8', 'B11', 'B4'], min:0, max:5000};
Map.addLayer(composite_free.first(), visParams, 'cloud mask');
// Create the NDVI and NDWI spectral indices.
var ndvi = Sen2mosaic.normalizedDifference(['B8', 'B4']);
var ndwi = Sen2mosaic.normalizedDifference(['B3', 'B8']);
var ndviEnd = Sen2mosaicEnd.normalizedDifference(['B8', 'B4']);
var ndwiEnd = Sen2mosaicEnd.normalizedDifference(['B3', 'B8']);
// Create some binary images from thresholds on the indices.
// This threshold is designed to detect bare land.
var bare1 = ndvi.lt(0.2).and(ndwi.lt(0.3));
var bare1End = ndviEnd.lt(0.2).and(ndwiEnd.lt(0.3));
// This detects bare land with lower sensitivity. It also detects shadows.
var bare2 = ndvi.lt(0.2).and(ndwi.lt(0.8));
var bare2End = ndviEnd.lt(0.2).and(ndwiEnd.lt(0.8));
// Define visualization parameters for the spectral indices.
var ndviViz = {min: -1, max: 1, palette: ['FF0000', '00FF00']};
var ndwiViz = {min: 0.5, max: 1, palette: ['00FFFF', '0000FF']};
// Mask and mosaic visualization images. The last layer is on top.
var thematic = ee.ImageCollection([
// NDWI > 0.5 is water. Visualize it with a blue palette.
ndwi.updateMask(ndwi.gte(0.5)).visualize(ndwiViz),
// NDVI > 0.2 is vegetation. Visualize it with a green palette.
ndvi.updateMask(ndvi.gte(0.2)).visualize(ndviViz),
// Visualize bare areas with shadow (bare2 but not bare1) as gray.
bare2.updateMask(bare2.and(bare1.not())).visualize({palette: ['AAAAAA']}),
// Visualize the other bare areas as white.
bare1.updateMask(bare1).visualize({palette: ['FFFFFF']}),
]).mosaic();
var thematicEnd = ee.ImageCollection([
ndwiEnd.updateMask(ndwiEnd.gte(0.5)).visualize(ndwiViz),
ndviEnd.updateMask(ndviEnd.gte(0.2)).visualize(ndviViz),
bare2End.updateMask(bare2End.and(bare1.not())).visualize({palette: ['AAAAAA']}),
bare1End.updateMask(bare1End).visualize({palette: ['FFFFFF']}),
]).mosaic();
//Map.addLayer(thematic, {}, 'thematic', false);
//Map.addLayer(thematicEnd, {}, 'thematic end', false);
//Map.addLayer(ndvi, {}, 'NDVI', false);
//Map.addLayer(ndviEnd, {}, 'NDVI end', false);
var Band_R = Sen2mosaic.expression('RED / count', {'RED': Sen2mosaic.select('B4'), count : Sen2.size()});
var Band_G = Sen2mosaic.expression('GREEN / count', {'GREEN': Sen2mosaic.select('B3'), count : Sen2.size()});
var Band_B = Sen2mosaic.expression('BLUE / count', {'BLUE': Sen2mosaic.select('B2'), count : Sen2.size()});
var Band_Rend = Sen2mosaicEnd.expression('RED / count', {'RED': Sen2mosaicEnd.select('B4'), count : Sen2.size()});
var Band_Gend = Sen2mosaicEnd.expression('GREEN / count', {'GREEN': Sen2mosaicEnd.select('B3'), count : Sen2.size()});
var Band_Bend = Sen2mosaicEnd.expression('BLUE / count', {'BLUE': Sen2mosaicEnd.select('B2'), count : Sen2.size()});
var image_RGB = Band_R.addBands(Band_G).addBands(Band_B);
var image_RGBend = Band_Rend.addBands(Band_Gend).addBands(Band_Bend);
var image_viz_params = {
//'bands': ['B5', 'B4', 'B3'],
'min': 2,
'max': 240,
gamma: 1.5,
//'gamma': [0.95, 1.1, 1]
};
Map.addLayer(image_RGB, image_viz_params, 'RGB 12/2018', false);
Map.addLayer(image_RGBend, image_viz_params, 'RGB 12/2020', false);
var dNDVI = ndviEnd.subtract(ndvi);
// Scale product to USGS standards
var dNDVIscaled = dNDVI.multiply(1000);
// Add the difference image to the console on the right
print("Difference Normalized Difference Vegetation Index: ", dNDVI);
//--------------------------- NDVI Product - classified -------------------------------
// Define an SLD style of discrete intervals to apply to the image.
var sld_intervals =
'<RasterSymbolizer>' +
'<ColorMap type="intervals" extended="false" >' +
'<ColorMapEntry color="#ff1b1b" quantity="-250" label="-250" />' +
'<ColorMapEntry color="#ffa81b" quantity="-100" label="-100" />' +
'<ColorMapEntry color="#f5ff1b" quantity="250" label="250" />' +
'<ColorMapEntry color="#1bff2f" quantity="500" label="500" />' +
'<ColorMapEntry color="#099b16" quantity="1000" label="1000" />' +
'</ColorMap>' +
'</RasterSymbolizer>';
Map.addLayer(dNDVIscaled.sldStyle(sld_intervals), {}, 'dNDVI classified');
//==========================================================================================
// ADD A LEGEND
// set position of panel
var legend = ui.Panel({
style: {
position: 'bottom-left',
padding: '8px 15px'
}});
// Create legend title
var legendTitle = ui.Label({
value: 'dNDVI Classes',
style: {fontWeight: 'bold',
fontSize: '18px',
margin: '0 0 4px 0',
padding: '0'
}});
// Add the title to the panel
legend.add(legendTitle);
// Creates and styles 1 row of the legend.
var makeRow = function(color, name) {
// Create the label that is actually the colored box.
var colorBox = ui.Label({
style: {
backgroundColor: '#' + color,
// Use padding to give the box height and width.
padding: '8px',
margin: '0 0 4px 0'
}});
// Create the label filled with the description text.
var description = ui.Label({
value: name,
style: {margin: '0 0 4px 6px'}
});
// return the panel
return ui.Panel({
widgets: [colorBox, description],
layout: ui.Panel.Layout.Flow('horizontal')
})};
// Palette with the colors
var palette =['ff1b1b', 'ffa81b', 'f5ff1b', '1bff2f', '099b16', 'ffffff'];
// name of the legend
var names = ['Forest Loss, High Probability', 'Forest Loss', 'Unchanged', 'Forest Gain, Low', 'Forest Gain, High Probability'];
// Add color and and names
for (var i = 0; i < 5; i++) {
legend.add(makeRow(palette[i], names[i]));
}
// add legend to map (alternatively you can also print the legend to the console)
Map.add(legend);
// Export a cloud-optimized GeoTIFF.
Export.image.toDrive({
image: dNDVIscaled,
description: 'imageToCOGeoTiffExample',
scale: 10,
region: AOI,
fileFormat: 'GeoTIFF',
formatOptions: {
cloudOptimized: true
}
});
https://code.earthengine.google.com/586a1c0df85e74d4df8871618a965f6a
Related
Green Chlorophyll index - Sentinel 2 GEE Palette
I am trying to load a map and be able to see the GRI(Green Chlorophyll index) on a certain area. I tried doing it with NDVI, however when I do it for GRI, I get an entirely black area. I'm not really sure what Im doing wrong especially that I'm new to this. Any tip would be really appreciated! var AOI = Boundary_Graf; var points = Points_Graf; //Display the exact location of the points var shp = ee.FeatureCollection(points); Map.addLayer(shp, {}, 'Graf.Points'); //Display the exact location of the polygon var shp = ee.FeatureCollection(AOI); Map.addLayer(shp, {}, 'Polygon_Graf'); //Open S2 var S2 = ee.ImageCollection("COPERNICUS/S2") //Filter for a specific date (now just chose it randomly) . filterDate("2022-01-01", "2022-06-22") //Filter for the polygon area . filterBounds(AOI) //Filter data based on metadata property -> cloudy pixel percentage <5% .filterMetadata('CLOUDY_PIXEL_PERCENTAGE', 'less_than', 5) // Subsetting the image based on AOI .map(function(image){return image.clip(AOI)}) // Printing the S2 layer to the console print ("S2 collection:", S2); var trueColour = {bands:["B8", "B4", "B3"], max: 3048, gamma:1}; var visParams = {min: 0, max: 0.8, palette: 'FFFFFF, CE7E45, DF923D, F1B555, FCD163, 99B718, 74A901, 66A000, 529400,' + '3E8601, 207401, 056201, 004C00, 023B01, 012E01, 011D01, 011301'}; // Calculate NDVI var NDVI_collection = S2.map(function(image){ var NDVI = image.normalizedDifference(['B8','B4']).rename('NDVI'); return NDVI .copyProperties(image, ['system:time_start']); }); // Calculate GCI var CGI_collection = S2.map (function(image){ var CGI = image.expression ('(((NIR) / (GREEN)) - 1)', { 'NIR': image.select ('B8'), 'GREEN': image.select ('B3') }).rename("GCI"); return CGI; }); Map.centerObject(AOI); Map.addLayer(S2, trueColour, 'true-colour image'); Map.addLayer(NDVI_collection, visParams,'Sentinel-2 NDVI'); Map.addLayer(CGI_collection, visParams, 'Sentinel-2 CGI');
cesium-Draw a set of lines in shape of circle on selected point
I am going to draw a set of lines in shape of circle from a clicked point.My code works fine but It draws the circle far away from clicked point.Can some one help me figure out where is my mistake? var viewer = new Cesium.Viewer('cesiumContainer'); var handler = new Cesium.ScreenSpaceEventHandler(viewer.canvas); handler.setInputAction(function(click) { var alertText = ''; function addToMessage(key, value) { alertText += key + ': ' + value + '\n'; } // var pickedObject = scene.pick(click.position); // addToMessage('target', pickedObject.id.id); var position = viewer.camera.pickEllipsoid(click.position); addToMessage('screenX', click.position.x); addToMessage('screenY', click.position.y); addToMessage('didHitGlobe', Cesium.defined(position)); var cartographicPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(position); addToMessage('longitude', cartographicPosition.longitude); addToMessage('latitude', Cesium.Math.toDegrees(cartographicPosition.latitude)); terrainSamplePositions = [cartographicPosition]; Cesium.sampleTerrain(viewer.terrainProvider, 9, terrainSamplePositions).then(function() { addToMessage('height', terrainSamplePositions[0].height); }).always(function() { // alert(alertText); }); // var x=Cesium.Math.toDegrees(cartographicPosition.latitude); // var y=Cesium.Math.toDegrees(cartographicPosition.longitude); var y=cartographicPosition.latitude; var x=cartographicPosition.longitude; // alert(x);//35 // alert(y);//-101 var radius=0.01; var segmants=360; var seg=Math.PI*2/segmants; var origin= new Cesium.Cartesian3(x, y, 0); var shape = []; for (i = 0; i <= segmants; i++) { var teta=seg*i; var a =x+Math.cos(teta)*radius; var b =y+Math.sin(teta)*radius; // shape.push(a); // shape.push(b); var origin= new Cesium.Cartesian3(x, y, 0); console.log(["origin is",origin.y,origin.x]); var direction= new Cesium.Cartesian3(a, b, 0); console.log(["direction is",direction.y,direction.x]); viewer.entities.add({ name : 'Red line on the surface', polyline : { positions : Cesium.Cartesian3.fromCartographicArray([origin.x, origin.y, direction.x, direction.y]), width : 5, material : Cesium.Color.RED } }); } viewer.zoomTo(resultPolygon.position); }, Cesium.ScreenSpaceEventType.LEFT_CLICK); thanks for your help
If want to draw a circle, simpler to draw an ellipse geometry with an outline. For a circle, set semiMinorAxis with same value as semiMajorAxis whose value is in meters. var position = viewer.camera.pickEllipsoid(click.position); var cartographicPosition = Cesium.Ellipsoid.WGS84.cartesianToCartographic(position); var y = cartographicPosition.latitude; var x = cartographicPosition.longitude; var entity = viewer.entities.add({ position: Cesium.Cartesian3.fromRadians(x, y), ellipse : { semiMinorAxis : 10, semiMajorAxis : 10, fill : false, outline: true, material : Cesium.Color.RED, outlineColor : Cesium.Color.RED, outlineWidth : 2 } }); viewer.zoomTo(entity);
Use the HoverTool in BokehJS
I wrote some javascript to create a correlation plot in BokehJS (everything happens in the client, I can't use the Python Bokeh package). Now, I would like to add a HoverTool to display tooltips when the user hovers over the squares, but I can't find documentation or examples on how to do this. I started looking at the coffeescript source and found relavant pieces, but I don't really understand how to integrate them. Any help finding documentation or examples about how to use the HoverTool in pure BokehJS would be great.
This is a pure javascript version that works with Bokeh 0.12.4. It shows how to use grid plot, with separate hovers over each set of data. The {0.01} in the hover object is used to format the values to 2 decimal places. var plt = Bokeh.Plotting; var colors = [ '#ee82ee', '#523ebf', '#9bc500', '#ffb600', '#f50019', '#511150', '#8b38fa', '#2e792a', '#ffef00', '#ff7400', '#a90064', '#000000' ] function circlePlots(xyDict, plot_width, plot_height, title) { // make the plot and add some tools var tools = "pan,crosshair,wheel_zoom,box_zoom,reset"; var p = plt.figure({ title: title, plot_width: plot_width, plot_height: plot_height, tools: tools }); // call the circle glyph method to add some circle glyphs var renderers = []; for (var i = 0; i <= 3; i += 1) { // create a data source var thisDict = { 'x': xyDict['x'], 'y': xyDict['y'][i], 'color': xyDict['color'][i] } var source = new Bokeh.ColumnDataSource({ data: thisDict }); var r = p.circle({ field: "x" }, { field: 'y' }, { source: source, fill_color: colors[i], fill_alpha: 0.6, radius: 0.2 + 0.05 * i, line_color: null }); renderers.push(r); } var tooltip = ("<div>x: #x{0.01}</div>" + "<div>y: #y{0.01}</div>" + "<div>color: #color</div>"); var hover = new Bokeh.HoverTool({ renderers: renderers, tooltips: tooltip }); p.add_tools(hover); return p } var pageWidth = 450; var plotCols = 2; var plots = []; var plotWidth = Math.floor(pageWidth / plotCols) if (plotWidth > 600) { plotWidth = 600 } var plotHeight = Math.floor(0.85 * plotWidth) for (var i = 0; i < plotCols; i += 1) { // set up some data var M = 20; var xyDict = { y: [], color: [] }; for (var j = 0; j <= 4; j += 1) { xyDict['x'] = []; xyDict['y'].push([]); xyDict['color'].push([]); for (var x = 0; x <= M; x += 0.5) { xyDict['x'].push(x); xyDict['y'][j].push(Math.sin(x) * (j + 1) * (i + 1)); xyDict['color'][j].push(colors[j]); } } var title = "Sin(x) Plot " + (i + 1).toString(); var p = circlePlots(xyDict, plotWidth, plotHeight, title); plots.push(p) }; plt.show(plt.gridplot([plots], sizing_mode = "stretch_both")); <link href="https://cdn.bokeh.org/bokeh/release/bokeh-0.12.4.min.css" rel="stylesheet" /> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-0.12.4.min.js"></script> <script src="https://cdn.bokeh.org/bokeh/release/bokeh-api-0.12.4.min.js"></script>
First to use hover in bokeh you must add it as a tool and then personalise it to show what will be shown on hover. Look at this US Unemployment example from the bokeh docs TOOLS = "hover,save" p = figure(title="US Unemployment (1948 - 2013)", x_range=years, y_range=list(reversed(months)), x_axis_location="above", plot_width=900, plot_height=400, toolbar_location="left", tools=TOOLS) hover = p.select(dict(type=HoverTool)) hover.tooltips = OrderedDict([ ('date', '#month #year'), ('rate', '#rate'), ])
NVD3 - How to refresh the data function to product new data on click
I have a line chart and every time the page refresh it changes the data, which is great but I need to to refresh by a user click. This is because there will eventually be other input fields on the page and refreshing the page would destroy their current session. jsfiddle - http://jsfiddle.net/darcyvoutt/dXtv2/ Here is the code setup to create the line: function economyData() { // Rounds var numRounds = 10; // Stability of economy var stable = 0.2; var unstable = 0.6; var stability = unstable; // Type of economy var boom = 0.02; var flat = 0; var poor = -0.02; var economyTrend = boom; // Range var start = 1; var max = start + stability; var min = start - stability; // Arrays var baseLine = []; var economy = []; // Loop for (var i = 0; i < numRounds + 1; i++) { baseLine.push({x: i, y: 1}); if (i == 0) { economyValue = 1; } else { var curve = Math.min(Math.max( start + ((Math.random() - 0.5) * stability), min), max); economyValue = Math.round( ((1 + (economyTrend * i)) * curve) * 100) / 100; } economy.push({x: i, y: economyValue}); } return [ { key: 'Base Line', values: baseLine }, { key: 'Economy', values: economy } ]; } Here is what I tried to write but failed for updating: function update() { sel = svg.selectAll(".nv-line") .datum(data); sel .exit() .remove(); sel .enter() .append('path') .attr('class','.nv-line'); sel .transition().duration(1000); }; d3.select("#update").on("click", data);
Here is what I did differently with your code. // Maintian an instance of the chart var chart; // Maintain an Instance of the SVG selection with its data var chartData; nv.addGraph(function() { chart = nv.models.lineChart().margin({ top : 5, right : 10, bottom : 38, left : 10 }).color(["lightgrey", "rgba(242,94,34,0.58)"]) .useInteractiveGuideline(false) .transitionDuration(350) .showLegend(true).showYAxis(false) .showXAxis(true).forceY([0.4, 1.6]); chart.xAxis.tickFormat(d3.format('d')).axisLabel("Rounds"); chart.yAxis.tickFormat(d3.format('0.1f')); var data = economyData(); // Assign the SVG selction chartData = d3.select('#economyChart svg').datum(data); chartData.transition().duration(500).call(chart); nv.utils.windowResize(chart.update); return chart; }); Here's how the update() function looks like: function update() { var data = economyData(); // Update the SVG with the new data and call chart chartData.datum(data).transition().duration(500).call(chart); nv.utils.windowResize(chart.update); }; // Update the CHART d3.select("#update").on("click", update); Here is a link to a working version of your code. Hope it helps.
Spring between two draggable surfaces in famo.us
I'm trying to implement two surfaces, connected with a spring, that would react to drag in famo.us. So far I have setup the surfaces, can drag those, have a spring that interacts during the loading of the page, but not on drag. So the questions are a) how should I connect two surfaces with a spring and b) how do I update the physics when I drag one surface so that the other surface would follow the dragged surface? The code I so far have is this define(function(require) { var Engine = require('famous/core/Engine'); var Surface = require('famous/core/Surface'); var StateModifier = require('famous/modifiers/StateModifier'); var PhysicsEngine = require('famous/physics/PhysicsEngine'); var Circle = require('famous/physics/bodies/Circle'); var Draggable = require('famous/modifiers/Draggable'); var Spring = require('famous/physics/forces/Spring'); var Vector = require('famous/math/Vector'); var context = Engine.createContext(); var physicsEngine = new PhysicsEngine(); var ball = new Surface ({ size: [100,100], properties: { backgroundColor: 'red', borderRadius: '50px' } }); var ball2 = new Surface ({ size: [100,100], properties: { backgroundColor: 'blue', borderRadius: '50px' } }); var draggable = new Draggable(); var draggable2 = new Draggable(); ball.state = new StateModifier({origin:[0.2,0.2]}); ball2.state = new StateModifier({origin:[0.3,0.3]}); ball.particle = new Circle({radius:100}); ball2.particle = new Circle({radius:100}); var spring = new Spring({ anchor: ball.particle, period: 400, // <= Play with these values :-) dampingRatio: 0.07, // <= length: 50 }); // var spring2 = new Spring({anchor: ball2.particle}); // physicsEngine.attach(spring, ball2.particle); // physicsEngine.attach(spring2, ball.particle); draggable.subscribe(ball); draggable2.subscribe(ball2); draggable.on('update', function() { console.info('update'); ball2.particle.applyForce(new Vector(0, 0, -0.005 * 100)); // ball.state.setTransform(ball.particle.getTransform()) // ball.state.setTransform(ball.particle.getTransform()) // ball.particle.setVelocity([0.001,0,0]); // physicsEngine.wake(); // physicsEngine.step(); }); draggable2.on('update', function() { // ball2.particle.setVelocity([0.001,0,0]); // console.info('update'); // physicsEngine.wake(); // physicsEngine.step(); }); physicsEngine.attach(spring, ball2.particle); // spring.applyForce(ball.particle); physicsEngine.addBody(ball.particle); physicsEngine.addBody(ball2.particle); // ball.on("click",function(){ // ball.particle.setVelocity([10,0,0]); // }); // // ball2.on("click",function(){ // ball2.particle.setVelocity([0,10,0]); // }); context.add(draggable).add(ball.state).add(ball); context.add(draggable2).add(ball2.state).add(ball2); Engine.on('prerender', function(){ ball.state.setTransform(ball.particle.getTransform()); ball2.state.setTransform(ball2.particle.getTransform()); }); });
It seems like you have a pretty good understanding of the PE thus far. I can still see a few places you can improve. Here is a working example of dragging with a spring attached. Although this implementation is not perfect yet, it should get you started.. If you start with dragging the red circle, everything works as expected.. Draggable has its own position, and so does particle. So when you grab the blue circle, there remains an offset in particle. Here is what will get you 95%.. Hope it helps.. var Engine = require('famous/core/Engine'); var Surface = require('famous/core/Surface'); var Transform = require('famous/core/Transform'); var Modifier = require('famous/core/Modifier'); var Draggable = require('famous/modifiers/Draggable'); var PhysicsEngine = require('famous/physics/PhysicsEngine'); var Circle = require('famous/physics/bodies/Circle'); var Spring = require('famous/physics/forces/Spring'); var context = Engine.createContext(); var physicsEngine = new PhysicsEngine(); var ball = new Surface ({ size: [100,100], properties: { backgroundColor: 'red', borderRadius: '50px' } }); var ball2 = new Surface ({ size: [100,100], properties: { backgroundColor: 'blue', borderRadius: '50px', } }); ball.mod = new Modifier({origin:[0.5,0.5]}); ball.draggable = new Draggable(); ball.pipe(ball.draggable); ball.particle = new Circle({radius:100}); ball.mod.transformFrom(function(){ return Transform.translate(0,0,0) }); ball.spring = new Spring({ anchor: ball.particle, period: 400, dampingRatio: 0.07, length: 50 }); ball2.mod = new Modifier({origin:[0.5,0.5]}); ball2.draggable = new Draggable(); ball2.pipe(ball2.draggable); ball2.particle = new Circle({radius:100}); ball2.mod.transformFrom(function(){ return ball2.particle.getTransform()}); ball2.spring = new Spring({ anchor: ball2.particle, period: 400, dampingRatio: 0.07, length: 50 }); ball.draggable.on('start',function(){ ball2.setProperties({pointerEvents:'none'}); if (ball2.springID) physicsEngine.detach(ball2.springID); if (ball.springID) physicsEngine.detach(ball.springID); ball.springID = physicsEngine.attach(ball.spring, ball2.particle); ball2.springID = null; ball.mod.transformFrom(function(){ return Transform.translate(0,0,0) }); ball2.mod.transformFrom(function(){ return ball2.particle.getTransform()}); }) ball.draggable.on('update', function() { pos = ball.draggable.getPosition(); ball.particle.setPosition(pos); }); ball.draggable.on('end', function() { ball2.setProperties({pointerEvents:'all'}); }); ball2.draggable.on('start',function(){ ball.setProperties({pointerEvents:'none'}); if (ball2.springID) physicsEngine.detach(ball2.springID); if (ball.springID) physicsEngine.detach(ball.springID); ball2.springID = physicsEngine.attach(ball2.spring, ball.particle); ball.springID = null; ball2.mod.transformFrom(function(){ return Transform.translate(0,0,0) }); ball.mod.transformFrom(function(){ return ball.particle.getTransform()}); }) ball2.draggable.on('update', function() { pos = ball2.draggable.getPosition(); ball2.particle.setPosition(pos); }); ball2.draggable.on('end', function() { ball.setProperties({pointerEvents:'all'}); }); ball.springID = physicsEngine.attach(ball.spring, ball2.particle); physicsEngine.addBody(ball.particle); physicsEngine.addBody(ball2.particle); context.add(ball.mod).add(ball.draggable).add(ball); context.add(ball2.mod).add(ball2.draggable).add(ball2);