CollectionFS get image dimensions before transformation - meteor

I would like to resize image upon upload with Meteor CollectionFS. But I would like to resize based on image dimensions. For example, I want to resize image that is 1000x500 into 1024x512, but 60x100 into 64x128 - for that I need to know source dimensions.
I am basing my code on one provided by CollectionFS documentation:
var createThumb = function(fileObj, readStream, writeStream) {
// Transform the image into a 10x10px thumbnail
gm(readStream, fileObj.name()).resize('10', '10').stream().pipe(writeStream);
};
how do I get source dimensions here, to make my resize target dynamic? Maybe there are some graphicsmagick function?

There's an async GraphicsMagick function for returning the size (dimensions) of the image.
gm(readStream).size({ bufferStream: true }, function(err, value) {
var w = 100, h = 100;
//modify logic here to set the desired output width/height, based on the source width/height
if (value.width == 60 && value.height == 100) {
w = 64;
h = 128;
}
//"this" is the gm context, so you can chain gm functions to "this" inside the size callback.
this.resize(w, h).stream().pipe(writeStream);
});
Notes from the gm npm package page on Github regarding this:
GOTCHA: when working with input streams and any 'identify' operation
(size, format, etc), you must pass "{bufferStream: true}" if you also
need to convert (write() or stream()) the image afterwards NOTE: this
buffers the readStream in memory!

Related

image.filter is not a function in google earth engine

As a newbie to the google earth engine, I have been trying something (https://code.earthengine.google.com/6f45059a59b75757c88ce2d3869fc9fd) following a NASA tutorial (https://www.youtube.com/watch?v=JFvxudueT_k&ab_channel=NASAVideo). My last line (line 60) shows image.filter is not a function, while the one in the tutorial (line 34) is working. I am not sure what happened and how to sort this out?
//creating a new variable 'image' from the L8 collection data imported
var image = ee.Image (L8_tier1 //the details in the data will represent that the band resolution is 30m
//the details in the data will represent that the band resolution is 30m
//.filterDate ("2019-07-01","2021-10-03") //for a specific date range. maybe good to remove it for the function.
//the details in the data will represent that the band resolution is 30m
//the details in the data will represent that the band resolution is 30m
//.filterDate ("2019-07-01","2021-10-03") //for a specific date range. maybe good to remove it for the function.
.filterBounds (ROI) //for the region of interest we are interested in
//.sort ("COLUD_COVER") //for sorting the data between the range with a cloud cover, the metadata property we are interested in. Other way to do this is using the function below.
//.first() //this will make the image choose the first image with the least amount of cloud cover for the area. Other way to do this is using the function below.
);
//print ("Hague and Rotterdam", image); //printing the image in the console
//console on the right hand side will explain everything from the data
//id will show the image deatils and date of the image, for this case 29th July 2019
//under the properties tab cloud cover can be found, this is the least we can get for this area during this period
// //vizualisation of the data in the map with true color rendering
// var trueColour = {
// bands:["SR_B4","SR_B3","SR_B2"],
// min: 5000,
// max: 12000
// };
// Map.centerObject (ROI, 12); //for the centering the area in the center of the map with required zoom level
// Map.addLayer (image, trueColour, "Hague and Rotterdam"); //for adding the image with the variable of bands we made and naming the image
//Alternate way
//Function to cloud mask from the qa_pixel band of Landsat 8 SR data. In this case bits 3 and 4 are clouds and cloud shadow respectively. This can be different for different image sets.
function maskL8sr(image) {
var cloudsBitMask = 1 << 3; //remember to check this with the source
var cloudshadowBitMask = 1 << 4; //remember to check this with the source
var qa = image.select ('qa_pixel'); //creating the new variable from the band of the source image
var mask = qa.bitwiseAnd(cloudsBitMask).eq(0) //making the cloud equal to zero to mask them out
.and(qa.bitwiseAnd(cloudshadowBitMask).eq(0)); //making the cloud shadow equal to zero to mask them out
return image.updateMask(mask).divide(10000)
.select("SR_B[0-9]*")
.copyProperties(image, ["system:time_start"]);
}
// print ("Hague and Rotterdam", image);// look into the console now. How many images the code have downloaded!!!
//filtering imagery for 2015 to 2021 summer date ranges
//creating joint filter and applying to image collection
var sum21 = ee.Filter.date ('2021-06-01','2021-09-30');
var sum20 = ee.Filter.date ('2020-06-01','2020-09-30');
var sum19 = ee.Filter.date ('2019-06-01','2019-09-30');
var sum18 = ee.Filter.date ('2018-06-01','2018-09-30');
var sum17 = ee.Filter.date ('2017-06-01','2017-09-30');
var sum16 = ee.Filter.date ('2016-06-01','2016-09-30');
var sum15 = ee.Filter.date ('2015-06-01','2015-09-30');
var SumFilter = ee.Filter.or(sum21, sum20, sum19, sum18, sum17, sum16, sum15);
var allsum = image.filter(SumFilter);
Filtering is an operation you can do on ImageCollections, not individual Images, because all filtering does is choose a subset of the images. Then, in your script, you have (with the comments removed):
var image = ee.Image (L8_tier1
.filterBounds (ROI)
);
The result of l8_tier1.filterBounds(ROI) is indeed an ImageCollection. But in this case, you have told the Earth Engine client that it should be treated as an Image, and it believed you. So, then, the last line
var allsum = image.filter(SumFilter);
fails with the error you saw because there is no filter() on ee.Image.
The script will successfully run if you change ee.Image(...) to ee.ImageCollection(...), or even better, remove the cast because it's not necessary — that is,
var image = L8_tier1.filterBounds(ROI);
You should probably also change the name of var image too, since it is confusing to call an ImageCollection by the name image. Naming things accurately helps avoid mistakes, while you are working on the code and also when others try to read it or build on it.

apply a function over 2 consecutive images in an imageCollection in google earth engine

the function .map applies a function to every individual image in an ImageCollection. And the function .iterate applies a function to one image and the output of the calculation done to the precedent image on an ImageCollection.
The first only works with one image each time, and the second implies modifying each image and utilize it to any calculation with the next one.
I need a function that works like .iterate, but does not modify the precedent image. I just need to do:
image (time -1) / image (time 0).
I can not find a way to do it,
thanks for your help
i have tried,
var first = ee.List([
ee.Image(1).set('system:time_start', time0).select([0], ['pc1'])
]);
var changeDET = function(image, list) {
var previous = ee.Image(ee.List(list).get(-1));
var change = previous.divide(image.select('pc1'))
.set('system:time_start', image.get('system:time_start'));
return ee.List(list).add(change);
};
var cumulative = ee.ImageCollection(ee.List(imageCollection.iterate(changeDET, first)))
.sort('system:time_start', false)
What you can do is to convert your imageCollection into a ee.List object, then map over that list with an index variable to access the previous image. Example code is below:
var length = yourImageCollection.size();
var list = yourImageCollection.toList(length);
var calculated_list = list.map(function(img) {
var index = list.indexOf(img)
img = ee.Image(img);
var previousIndex = ee.Algorithms.If(index.eq(0), index, index.subtract(1));
var previousImage = ee.Image(list.get(previousIndex)):
var change = ee.Image(previousImage.divide(img)
.copyProperties(img, ["system:time_start"]));
return change;
})
I'm not sure what you want to do with the first image, so when map function reach the first image, previousIndex will equal to index. In other words, the first image will be divided by itself (as there is no image before it).
Hope this helps.

How to increase resolution of exported image in Google Earth Engine?

I have some code to export an LS8 image to Drive using GEE. Somehow, the resolution of the image seems to be lower (larger pixels) than what I am able to see on the browser. How can I increase the resolution? This is the code I´ve been using, with two different options.
I attempted to use .resample() as a solution but it produced a single band image that did not look good.
geometry2 = /* color: #57d64b */ee.Geometry.Polygon(
[[[-78.2812086867645, 2.3386717366200585],
[-77.56984394067075, 2.3729749945579948],
[-77.72227924340513, 2.776314152654142],
[-78.20842426293638, 2.725560942159387]]]);
function maskL8sr(image)
{
// Bits 3 and 5 are cloud shadow and cloud, respectively.
var cloudShadowBitMask = ee.Number(2).pow(3).int();
var cloudsBitMask = ee.Number(2).pow(5).int();
// Get the pixel QA band.
var qa = image.select('pixel_qa');
// Both flags should be set to zero, indicating clear conditions.
var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
.and(qa.bitwiseAnd(cloudsBitMask).eq(0));
// Return the masked image, scaled to TOA reflectance, without the QA bands.
return image.updateMask(mask).divide(10000)
.select("B[0-9]*")
.copyProperties(image, ["system:time_start"]);
}
// Map the function over one year of data.
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
.filterDate('2016-04-01', '2018-7-31')
.map(maskL8sr)
var composite = collection.median();
var VIS = {bands: ['B5', 'B6', 'B4'], min: 0, max: 0.47};
// Display the results.
Map.addLayer(composite,VIS ,"compt2");
var params ={
crs: 'EPSG:4326',
maxPixels: 1e12,
region:geometry2
}
Export.image(composite,'Guapi',params);
Export.image.toDrive({
image: composite,
description: 'Guapi',
scale: 30,
region: geometry2,
maxPixels: 1000000000
});

Matter.js Gravity Point

Is it possible to create a single gravity / force point in matter.js that is at the center of x/y coordinates?
I have managed to do it with d3.js but wanted to enquire about matter.js as it has the ability to use multiple polyshapes.
http://bl.ocks.org/mbostock/1021841
The illustrious answer has arisen:
not sure if there is any interest in this. I'm a fan of what you have created. In my latest project, I used matter-js but I needed elements to gravitate to a specific point, rather than into a general direction. That was very easily accomplished. I was wondering if you are interested in that feature as well, it would not break anything.
All one has to do is setting engine.world.gravity.isPoint = true and then the gravity vector is used as point, rather than a direction. One might set:
engine.world.gravity.x = 355;
engine.world.gravity.y = 125;
engine.world.gravity.isPoint = true;
and all objects will gravitate to that point.
If this is not within the scope of this engine, I understand. Either way, thanks for the great work.
You can do this with the matter-attractors plugin. Here's their basic example:
Matter.use(
'matter-attractors' // PLUGIN_NAME
);
var Engine = Matter.Engine,
Events = Matter.Events,
Runner = Matter.Runner,
Render = Matter.Render,
World = Matter.World,
Body = Matter.Body,
Mouse = Matter.Mouse,
Common = Matter.Common,
Bodies = Matter.Bodies;
// create engine
var engine = Engine.create();
// create renderer
var render = Render.create({
element: document.body,
engine: engine,
options: {
width: Math.min(document.documentElement.clientWidth, 1024),
height: Math.min(document.documentElement.clientHeight, 1024),
wireframes: false
}
});
// create runner
var runner = Runner.create();
Runner.run(runner, engine);
Render.run(render);
// create demo scene
var world = engine.world;
world.gravity.scale = 0;
// create a body with an attractor
var attractiveBody = Bodies.circle(
render.options.width / 2,
render.options.height / 2,
50,
{
isStatic: true,
// example of an attractor function that
// returns a force vector that applies to bodyB
plugin: {
attractors: [
function(bodyA, bodyB) {
return {
x: (bodyA.position.x - bodyB.position.x) * 1e-6,
y: (bodyA.position.y - bodyB.position.y) * 1e-6,
};
}
]
}
});
World.add(world, attractiveBody);
// add some bodies that to be attracted
for (var i = 0; i < 150; i += 1) {
var body = Bodies.polygon(
Common.random(0, render.options.width),
Common.random(0, render.options.height),
Common.random(1, 5),
Common.random() > 0.9 ? Common.random(15, 25) : Common.random(5, 10)
);
World.add(world, body);
}
// add mouse control
var mouse = Mouse.create(render.canvas);
Events.on(engine, 'afterUpdate', function() {
if (!mouse.position.x) {
return;
}
// smoothly move the attractor body towards the mouse
Body.translate(attractiveBody, {
x: (mouse.position.x - attractiveBody.position.x) * 0.25,
y: (mouse.position.y - attractiveBody.position.y) * 0.25
});
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/matter-js/0.12.0/matter.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/matter-attractors#0.1.6/build/matter-attractors.min.js"></script>
Historical note: the "gravity point" functionality was proposed as a feature in MJS as PR #132 but it was closed, with the author of MJS (liabru) offering the matter-attractors plugin as an alternate. At the time of writing, this answer misleadingly seems to indicate that functionality from the PR was in fact merged.
Unfortunately, the attractors library is 6 years outdated at the time of writing and raises a warning when using a newer version of MJS than 0.12.0. From discussion in issue #11, it sounds like it's OK to ignore the warning and use this plugin with, for example, 0.18.0. Here's the warning:
matter-js: Plugin.use: matter-attractors#0.1.4 is for matter-js#^0.12.0 but installed on matter-js#0.18.0.
Behavior seemed fine on cursory glance, but I'll keep 0.12.0 in the above example to silence it anyway. If you do update to a recent version, note that Matter.World is deprecated and should be replaced with Matter.Composite and engine.gravity.

Making images 300dpi from pngencoder

HI i have flex + php app.
Also i understand that i only get 96dpi from image, because of my monitor dpi, but I need to make sure that even it generates 96dpi, it really is 300dpi quality. Can somebody advise? I tried alivepdf, its not as good.
I am taking 28 original images from user and making a "thumbnail picture collage" out of it for printing. I dont get the crisp quality in generated collage images, compared to originals. Here are my function, can somebody help me improve my snapshot? I really need to generate photo quality images for printing. Thanks
private function createImages(object:Object):void
{
progress.text = "Start Generating Images ( "+(index+1)+" - 28 )";
images_array.push(ImageSnapshot.captureImage(album.tilesList[index],30 0,new PNGEncoder()));
}
private function uploadImage(snapshot:ImageSnapshot,name:String):void
{
var ba:ByteArray = snapshot.data;//PNGEnc.encode(snapshot);
//send data as normal files
ba.position = 0;
var ID:String = name;
var filename:String = ID+".png";
}
A monitor is 72dpi. If you want 300 dpi you should scale the image inside the pdf.
Your scale ratio should be 1 / (300 / 72) = 0.24;
So if you put the image inside a clip and set scaleX and scaleY to 0.24 and make a pdf of it; it 'is' 300dpi.

Resources