How can I pass a geometry to the Map function in GEE? - javascript

I am trying to use the Map function in Google Earth Engine to clip an ImageCollection to a geometry. I have multiple areas of interest (AOIs) and thus would like to apply a generic clip function multiple times (for each AOI). However, when I create a function with two parameters (the image and the geometry) to map over, I get the error image.clip is not a function. When using the function with just one parameter (the image), it works just fine, but then I need to write two (or possible more) functions to do exactly the same task (i.e. clipping an image to a certain geometry).
I have tried the solutions in this post, but they do not work.
Any ideas on how to solve this issue?
Code:
// Get NTL data
var ntl = ee.ImageCollection("NOAA/VIIRS/DNB/MONTHLY_V1/VCMSLCFG");
// Define start and end year
var startYear = 2015;
var endYear = 2018;
// Filter montly and select 'avg_rad' band
var ntlMonthly = ntl.filter(ee.Filter.calendarRange(startYear, endYear, 'year'))
.filter(ee.Filter.calendarRange(1,12,'month'))
.select(['avg_rad']);
// Create geometries of AOIs
// -- Create a geometry of Venezuela
var geomVenezuela = ee.Geometry.Rectangle([-73.341258, 13.363291, -59.637555, -0.372893]);
// -- Create a geometry of Caracas (Venezuela's capital)
var geomCaracas = ee.Geometry.Rectangle([-67.062383, 10.558489, -66.667078, 10.364908]);
// Functions to crop to Venezuela (nationwide) and Caracas (local) resp.
var clipImageToGeometry = function(image, geom) {
return image.clip(geom);
}
// Apply crop function to the ImageCollection
var ntlMonthly_Venezuela = ntlMonthly.map(clipImageToGeometry.bind(null, geomVenezuela));
var ntlMonthly_Caracas = ntlMonthly.map(clipImageToCaracas.bind(null, geomCaracas));
// Convert ImageCollection to single Image (for exporting to Drive)
var ntlMonthly_Venezuela_image = ntlMonthly_Venezuela.toBands();
var ntlMonthly_Caracas_image = ntlMonthly_Caracas.toBands();
// Check geometry in map
Map.addLayer(geomCaracas, {color: 'red'}, 'planar polygon');
Map.addLayer(ntlMonthly_Caracas_image);
// Store scale (m. per pixel) in variable
var VenezuelaScale = 1000;
var CaracasScale = 100;
// Export the image, specifying scale and region.
Export.image.toDrive({
image: ntlMonthly_Caracas_image,
description: 'ntlMonthly_Caracas_'.concat(startYear, "_to_", endYear),
folder: 'GeoScripting',
scale: CaracasScale,
fileFormat: 'GeoTIFF',
maxPixels: 1e9
});

If I understood your question correctly:
If you want to crop each image in the ImageCollection to a geometry, you could just do
var ntlMonthly_Venezuela = ntlMonthly.map(function(image){return ee.Image(image).clip(geomVenezuela)})
And just repeat for other AOIs.
If you wan to cast it into a function:
var clipImageCollection = function(ic, geom){
return ic.map(function(image){return ee.Image(image).clip(geom)})
}
// Apply crop function to the ImageCollection
var ntlMonthly_Venezuela = clipImageCollection(ntlMonthly, geomVenezuela);
var ntlMonthly_Caracas = clipImageCollection(ntlMonthly, geomCaracas);

Related

Exporting daily climatology data from NEX-GDDP product on Google Earth Engine

I am using NEX-GDDP for obtaining daily climatology (Precipitation, Temp min and Temp max) data of the 21 GCM models in the period 2018-01-01 to 2099-12-31, for certain points. I made this script, for one model in one scenario
//Dataset
var dataset = ee.ImageCollection('NASA/NEX-GDDP')
.filter(ee.Filter.date('2018-01-01', '2099-12-31'))
.filterMetadata('scenario','equals','rcp45')
.filterMetadata('model','equals','MPI-ESM-LR')
//Points of interest
var Acomayo = ee.Geometry.Point([-71.689166667, -13.921388889]),
var Machupicchu = ee.Geometry.Point([-72.545555556, -13.166666667]),
var Urubamba = ee.Geometry.Point([-72.129116546, -13.323123791]),
var Pisac = ee.Geometry.Point([-71.849444444, -13.415833333]),
var Ccatcca = ee.Geometry.Point([-71.56, -13.609722222]),
var GranjaKcayra = ee.Geometry.Point([-71.875, -13.556666667]),
var Pomacanchi = ee.Geometry.Point([-71.5357971, -14.027777778]),
var Sicuani = ee.Geometry.Point([-71.236944444, -14.253333333]);
var pts = ee.FeatureCollection(ee.List([ee.Feature(Acomayo),ee.Feature(Machupicchu),ee.Feature(Urubamba),ee.Feature(Pisac)
,ee.Feature(Ccatcca),ee.Feature(GranjaKcayra),ee.Feature(Pomacanchi),ee.Feature(Sicuani)]));
//Export to table .CSV
// Empty Collection to fill
var ft = ee.FeatureCollection(ee.List([]));
//Without removal of null values ----------------------------------
//Function to extract values from image collection based on point file and export as a table
var fill = function(img, ini) {
// type cast
var inift = ee.FeatureCollection(ini);
// gets the values for the points in the current img
var ft2 = img.reduceRegions(pts, ee.Reducer.first(),30);
// gets the date of the img
var date = img.date().format("yyyy/MM/dd");
var scenario = img.get("scenario");
var model = img.get("model");
// writes the date in each feature
var ft3 = ft2.map(function(f){return f.set("date", date, "scenario", scenario, "model", model)});
// merges the FeatureCollections
return inift.merge(ft3);
};
// Iterates over the ImageCollection
var newft = ee.FeatureCollection(dataset.iterate(fill, ft));
//print(newft);
// Export
Export.table.toDrive({
collection: newft,
description: 'GCM_diario',
folder: 'Downscalling_Diario',
fileFormat: 'csv'
});
The scripts work fine for two days and two points, but for the current points and period of time I need, it still working after 5 hrs. To reduce the amount of data I think these ideas:
Average the daily data of the 21 GCMs models in the product, and make it one ImgaeCollection, so
there is only a need to separate by scenario.
Export the ImageCollection of every variable (Pp, Tmin, Tmax) to a NetCDF only the area that contains points (don't know if it is
possible).
geometry = ee.Geometry.Polygon(
[[[-72.77555636882136, -12.867571480133547],
[-72.77555636882136, -14.670820732958893],
[-70.69914035319636, -14.670820732958893],
[-70.69914035319636, -12.867571480133547]]], null, false);
If there is another way to download this data I open to do it.
Using .iterate() can be very memory intensive and is prone to memory errors. A more straight forward approach to this would be to select a specific point you want to focus on, loop over all of the days of interest, and use .reduceRegion() to get the information desired. You can then export the time series as a CSV and convert it to whichever format you want.
Here is an example that gets all variables for all models and scenarios:
// specify start and end date
// Change as needed
var startDate = ee.Date('2018-01-01');
var endDate = ee.Date('2019-01-01');
// get the dataset between date range and extract band on interest
var dataset = ee.ImageCollection('NASA/NEX-GDDP')
.filter(ee.Filter.date(startDate,endDate));
// get projection and band information
var firstImage = dataset.first();
var bandNames = firstImage.bandNames();
var proj = firstImage.projection();
var point = ee.Geometry.Point([-71.689166667, -13.921388889])
// calculate number of days to map and extract data for
var n = endDate.difference(startDate,'day').subtract(1);
// map over each date and extract all climate model values
var timeseries = ee.FeatureCollection(
ee.List.sequence(0,n).map(function(i){
var t1 = startDate.advance(i,'day');
var t2 = t1.advance(1,'day');
var dailyColl = dataset.filterDate(t1, t2);
var dailyImg = dailyColl.toBands();
// rename bands to handle different names by date
var bands = dailyImg.bandNames();
var renamed = bands.map(function(b){
var split = ee.String(b).split('_');
return split.slice(0,2).cat(split.slice(-1)).join('_');
});
// extract the data for the day and add time information
var dict = dailyImg.rename(renamed).reduceRegion({
reducer: ee.Reducer.mean(),
geometry: point,
scale: proj.nominalScale()
}).combine(
ee.Dictionary({'system:time_start':t1.millis(),'isodate':t1.format('YYYY-MM-dd')})
);
return ee.Feature(point,dict);
})
);
print(timeseries);
// get properties to chart (all climate models)
var props = timeseries.first().propertyNames().removeAll(['system:time_start','system:index','isodate']);
// Make a chart of the results.
var chart = ui.Chart.feature.byFeature(timeseries, 'system:time_start', props.getInfo());
print(chart);
Map.addLayer(point);
Map.centerObject(point,6);
// export feature collection to CSV
Export.table.toDrive({
collection: timeseries,
description: 'NEX-GDDP-timeseries',
fileFormat: 'CSV',
});
You may run into memory errors in the code editor when working with long date ranges (2018-2099) but the export should work. Also, keep in mind that the Earth Engine exports are a little simplistic so exporting point by point would be the best approach, 1) to avoid memory errors and 2) keep the resulting CSV to one point. You can merge all the points together and export the time series per point in one file but that may be difficult to work with once exported...
Here is a working link: https://code.earthengine.google.com/139432f76ae3f6a81b1459762325ef7f

Earth Engine getThumbURL blurry image

I'm trying to find a way to specify a lat and long and retrieve a close up image. The code below allows me to enter a lat and long but the image is very blurry. Is there a simple way to get a higher resolution image?
My main issue is specifying the zoom level and I haven't found any examples of people retrieving close up images.
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_044034_20140318').select(['B4','B3','B2']);
// Create a circle with buffer around a point.
var roi = ee.Geometry.Point([-122.4481, 37.7599]).buffer(3000);
Map.centerObject(image, 15)
var a = image.getThumbURL({
image: image,
region:roi.getInfo()
});
//print URL
print(a);
You can add a dimensions parameter to the .getThumbURL() that will define the number of pixels in the output image. Here is the your example with a 2000x2000 output thumb:
var image = ee.Image('LANDSAT/LC08/C01/T1_TOA/LC08_044034_20140318').select(['B4','B3','B2']);
// Create a circle with buffer around a point.
var roi = ee.Geometry.Point([-122.4481, 37.7599]).buffer(3000);
Map.centerObject(image, 15)
var a = image.getThumbURL({
image: image,
dimensions:[2000,2000], // specify output thumb size here
region:roi.getInfo()
});
//print URL
print(a);
If you just want to see an image on the map (as displayed), you can do it using the UI facilities.
like:
var textbox = ui.Textbox({
placeholder: 'Point coordinates: long, lat',
onChange: function(text) {
var splitStr = text.split(",");
var lon = parseFloat(splitStr[0]);
var lat = parseFloat(splitStr[1]);
var p = ee.Geometry.Point(lon, lat);
Map.addLayer(p);
Map.centerObject(p, 12);
}
});
print(textbox);
This code will move the map to the given point coordinates and draw it.

Clipping one layer to the boundaries of another layer on google earth engine

so I'm trying to clip one layer (NDVI) to the boundaries of corn fields across Ontario. This is the code i have so far but it doesn't seem to be working. I'm not sure if you can actually clip layers to other layers, I know you can fore image collections but some input on how to solve this would be great. Thanks for the help.
var landcover_crops = ee.ImageCollection("AAFC/ACI")
// Load a collection of Landsat TOA reflectance images.
var landsatCollection = ee.ImageCollection('LANDSAT/LC08/C01/T1_TOA');
// The dependent variable we are modeling.
var dependent = 'NDVI';
// The number of cycles per year to model.
var harmonics = 1;
// Make a list of harmonic frequencies to model.
// These also serve as band name suffixes.
var harmonicFrequencies = ee.List.sequence(1, harmonics);
// Function to get a sequence of band names for harmonic terms.
var constructBandNames = function(base, list) {
return ee.List(list).map(function(i) {
return ee.String(base).cat(ee.Number(i).int());
});
};
// Construct lists of names for the harmonic terms.
var cosNames = constructBandNames('cos_', harmonicFrequencies);
var sinNames = constructBandNames('sin_', harmonicFrequencies);
// Independent variables.
var independents = ee.List(['constant', 't'])
.cat(cosNames).cat(sinNames);
// Function to mask clouds in Landsat 8 imagery.
var maskClouds = function(image) {
var score = ee.Algorithms.Landsat.simpleCloudScore(image).select('cloud');
var mask = score.lt(10);
return image.updateMask(mask);
};
// Function to add an NDVI band, the dependent variable.
var addNDVI = function(image) {
return image
.addBands(image.normalizedDifference(['B5', 'B4'])
.rename('NDVI'))
.float();
};
// Function to add a time band.
var addDependents = function(image) {
// Compute time in fractional years since the epoch.
var years = image.date().difference('2017-01-01', 'year');
var timeRadians = ee.Image(years.multiply(2 * Math.PI)).rename('t');
var constant = ee.Image(1);
return image.addBands(constant).addBands(timeRadians.float());
};
// Function to compute the specified number of harmonics
// and add them as bands. Assumes the time band is present.
var addHarmonics = function(freqs) {
return function(image) {
// Make an image of frequencies.
var frequencies = ee.Image.constant(freqs);
// This band should represent time in radians.
var time = ee.Image(image).select('t');
// Get the cosine terms.
var cosines = time.multiply(frequencies).cos().rename(cosNames);
// Get the sin terms.
var sines = time.multiply(frequencies).sin().rename(sinNames);
return image.addBands(cosines).addBands(sines);
};
};
// Filter to the area of interest, mask clouds, add variables.
var harmonicLandsat = landsatCollection
.filterBounds(geometry2)
.map(maskClouds)
.map(addNDVI)
.map(addDependents)
.map(addHarmonics(harmonicFrequencies));
// The output of the regression reduction is a 4x1 array image.
var harmonicTrend = harmonicLandsat
.select(independents.add(dependent))
.reduce(ee.Reducer.linearRegression(independents.length(), 1));
// Turn the array image into a multi-band image of coefficients.
var harmonicTrendCoefficients = harmonicTrend.select('coefficients')
.arrayProject([0])
.arrayFlatten([independents]);
// Compute fitted values.
var fittedHarmonic = harmonicLandsat.map(function(image) {
return image.addBands(
image.select(independents)
.multiply(harmonicTrendCoefficients)
.reduce('sum')
.rename('fitted'));
});
// Plot the fitted model and the original data at the ROI.
print(ui.Chart.image.series(fittedHarmonic.select(['fitted','NDVI']),
geometry2, ee.Reducer.mean(), 100)
.setOptions({
title: 'Harmonic model: original and fitted values',
lineWidth: 1,
pointSize: 3,
}));
// Pull out the three bands we're going to visualize.
var sin = harmonicTrendCoefficients.select('sin_1');
var cos = harmonicTrendCoefficients.select('cos_1');
// Do some math to turn the first-order Fourier model into
// hue, saturation, and value in the range[0,1].
var magnitude = cos.hypot(sin).multiply(5);
var phase = sin.atan2(cos).unitScale(-Math.PI, Math.PI);
var val = harmonicLandsat.select('NDVI').reduce('mean');
// Turn the HSV data into an RGB image and add it to the map.
var seasonality = ee.Image.cat(phase, magnitude, val).hsvToRgb();
Map.centerObject(geometry2, 11);
Map.addLayer(seasonality, {}, 'Seasonality');
Map.addLayer(geometry2, {}, 'corn_ndvi');
//need to change the image collection into a single image;
var crop2017 = landcover_crops
.filter(ee.Filter.date('2017-01-01', '2017-12-31'))//select for 2017 data
.first(); //collapses the data
//find what band names are, so we can filter the data on the right one
var bandNames = crop2017.bandNames();
print('Band names: ',bandNames);
//then need to select the band of interest...here there's only one band called
landcover
var crop2017_data=crop2017.select('landcover');
//then create various masks by selecting on the land cover value
var urban_mask = crop2017_data.eq(34); //creating the mask
var urban = crop2017_data.mask(urban_mask); //masking the data
var corn_mask = crop2017_data.eq(147);
var corn = crop2017_data.mask(corn_mask);
var soy_mask = crop2017_data.eq(158);
var soy = crop2017_data.mask(soy_mask);
var hay_mask = crop2017_data.eq(122);
var hay = crop2017_data.mask(hay_mask);
var grassland_mask = crop2017_data.eq(110);
var grassland = crop2017_data.mask(grassland_mask);
//Finally, add the masks to the map to make sure they are right
Map.addLayer(urban,undefined,'Urban');
Map.addLayer(corn,undefined,'Corn');
Map.addLayer(soy,undefined,'Soy');
Map.addLayer(hay,undefined,'Hay');
Map.addLayer(grassland,undefined,'Grassland');
//Can clip the mask to show just ontario
var crop2017_ontario = crop2017.clip(ontario);
var corn_ontario = corn.clip(ontario);
var soy_ontario = soy.clip(ontario);
var urban_ontario = urban.clip(ontario);
Map.addLayer(crop2017_ontario,undefined,'All Crops Ontario');
Map.addLayer(corn_ontario,undefined,"Corn Ontario");
Map.addLayer(soy_ontario,undefined,'Soy Ontario');
Map.addLayer(urban_ontario,undefined,'Urban Ontario');
// Composite an image collection and clip it to a boundary.
// Clip to the output image to the Nevada and Arizona state boundaries.
var clipped = seasonality.clipToCollection(corn_ontario);
// Display the result.
Map.setCenter(-80.24, 43.54);
Map.addLayer(clipped, 'clipped composite');
If you would like to clip the NDVI image to the actual bounds of the corn image you can use var clipped = ndvi.clip(corn.geometry()). Every image has a geometry object associated with it and the clip function needs a geometry as an input.
If you want to only keep the NDVI values where the corn is then you should use var masked = ndvi.updateMask(corn). This will only keep NDVI pixels that are also corn pixels.
I tried running your code, it seems that some geometries are not available in the code you posted so it was difficult to identify what exactly was wrong with your code. I hope this helps! If not, just send the link to the code you had working and which line was causing the issue.

How to connect objects with Bezier-like curves using Paper.js

I have a web app prototype where nodes similar to Blender shader editor are connected to each other. I am using Paper.js framework
I want them to be connected using those smooth Bezier-like curves. So I have 2 shapes and I can connect them by making a straight line, but now I want to have handles at the endpoints that smooth these objects out, kinda like this:
So 2 handles on endpoints, pointing horizontally for half the bounding box of the path.
The problem is I can't figure out how to add and edit those handles using Paper.js
The code I have is this:
function makeRectangle(topLeft, size, cornerSize, colour){
var rectangle = new Rectangle(topLeft, size);
var cornerSize = cornerSize;
var path = new Path.RoundRectangle(rectangle, cornerSize);
path.fillColor = colour;
return path;
}
var xy1 = new Point(50,50); //Position of 1st rectangle.
var size = new Size(100, 80); //Size
var c = new Size(8,8); //Corner radius
var col = "#167ee5"; //Colour
var r1 = makeRectangle(xy1, size, c, col); //Make first rectangle
var xy2 = new Point(467,310); //Position of second rectangle
var size2 = new Size(115, 70); //Size of second rectangle
var r2 = makeRectangle(xy2, size2, c, col); //Make secont rectangle
var r1cent = r1.bounds.center; //Get the center points, they will be used as endpoints for the curve.
var r2cent = r2.bounds.center;
var connector = new Path(r1cent, r2cent); //Ok so I made this path... Now what? How do access and edit the handlers at endpoints like in the image?
connector.strokeColor = 'black'; //Give it some colour so we can see it.
You can paste all this code here without any setup, it's a good way to test the framework.
You can use Segment objects when defining the connector rather than using Points (or you can set the handleIn and handleOut properties after creating the path).
The doc is here: Segment
And here is a sketch showing how to use handleIn and handleOut with your code:
sketch.paperjs.org solution

OpenLayers - Mosaic of custom images on one layer

Is it possible to have a layer made up of a mosaic of custom images?
I've been only able to get a single custom image on a given layer via OpenLayers.Layer.Image. Essentially, if I could find a way to specify custom images for the tiles of a given layer, then my problem would be solved.
I have tried various combinations of OpenLayers.Tile, OpenLayers.Tile.Image, OpenLayers.Layer and OpenLayers.Layer.Grid but haven't been able to things working.
The basic flow I follow is:
var map = new OpenLayers.Map('map');
var layer = new <OpenLayers.Layer | OpenLayers.Layer.Grid> (<parameters>);
var tile1 = new <OpenLayers.Tile | OpenLayers.Tile.Image> (<parameters>);
map.addLayer(layer);
map.zoomToMaxExtent();
Specific examples of how I initialize each constructor are provided below.
Regarding OpenLayers.Layer.Grid, I'm actually not sure what to specify for the url and params constructor parameters.
Any advice on whether this works and/or if I'm on the right track would be greatly appreciated.
OpenLayers.Layer
var layer = new OpenLayers.Layer(
'layer_name',
{
isBaseLayer: true
}
);
OpenLayers.Layer.Grid
var layer = new OpenLayers.Layer.Grid(
'layer_name',
?url?,
?params?
);
OpenLayers.Tile
var layer = new OpenLayers.Tile(
layer_name,
new OpenLayers.Pixel(0,0),
new OpenLayers.Bounds(-1,-1,1,1),
'square1.jpg',
new OpenLayers.Size(300,300)
);
OpenLayers.Tile.Image
var layer = new OpenLayers.Tile.Image(
layer_name,
new OpenLayers.Pixel(0,0),
new OpenLayers.Bounds(-1,-1,1,1),
new OpenLayers.Size(300,300),
{
url: 'square1.jpg'
}
);
Have you tried Zoomify layer? Here's the example. It allows you to load in the map all images from a given directory, named in the form {z}-{x}-{y}.jpg, where {z} is the zoom level.
If you need to break up an image into smaller tiles, I suggest using this free MapTiler software, which will create the tiles for as many zoom levels as you need.
You can create a map layer of image tiles using a TMS Layer:
var layer = new OpenLayers.Layer.TMS("TMS Layer","",
{url: '', serviceVersion: '.', layername: '.', alpha: true,
type: 'png', getURL: getTileURL
}
);
map.addLayer(layer);
The getTileURL function is used by the TMS layer to find the tiled images to display. This function assumes that the images are stored in a hierarchical structure like the one created by MapTiler.
Ex: img/tiles/7/4/1.png is the image that is 5th from the left and 2nd from the bottom of zoom level 7.
function getTileURL(bounds)
{
var res = this.map.getResolution();
var x = Math.round((bounds.left - this.maxExtent.left) / (res * this.tileSize.w));
var y = Math.round((bounds.bottom - this.maxExtent.bottom) / (res * this.tileSize.h));
var z = this.map.getZoom();
var path = "img/tiles/" + z + "/" + x + "/" + y + "." + this.type;
var url = this.url;
if (url instanceof Array)
{
url = this.selectUrl(path, url);
}
return url + path;
}

Categories

Resources