Gaps in rasters as a result of OBIA. Google Earth Engine - google-earth-engine

I am running an object-oriented classification and I get gaps in the rasters that I want to use. 
For example, I want to use elevation data from 'USGS/SRTMGL1_003'. If you run the code, you will see a gap in the elevation layer. I believe something happens when I use the command .reduceConnectedComponents, because the areas with gaps are present in the original images, in the clusters obtained as a result of the SNIC algorithm, and in SRTM. The same thing happens when I set the object size and reproject the rasters. The gaps are different in size, but still present. Since I haven't reprojected it in this code, they will change as you zoom in/out, but they persist even when I set a scale and export the files.
This happens to all the metrics that I want to use (after applying the .reduceConnectedComponents command), not only elevation. My images have a spatial resolution of 2.5 m. 
The code is in this link or below:
var clip = ee.FeatureCollection('users/rizayeva/DS1011-1040_geometry');
var image2 = ee.Image('users/rizayeva/corona_2_5m/DS1011-1040DA_2_5m_JPEG_1band_EPSG32638').rename('ds10111040da');
var image3 = ee.Image('users/rizayeva/corona_2_5m/DS1011-1040DF_2_5m_JPEG_1band_EPSG32638').rename('ds10111040df');
var img = ee.Image.cat([image2, image3]).divide(255);
Map.addLayer(img, {bands: ["ds10111040df", "ds10111040df", "ds10111040da"], gamma: 0.8}, 'DS1011-1040');
Map.centerObject(ee.Geometry.Point(44.5567, 41.7856), 10);
var elevation = ee.Image('USGS/SRTMGL1_003').select('elevation').clip(clip);
Map.addLayer(elevation, {min: 1, max: 3445,  gamma: [1.85], opacity: 1}, 'Elevation', false);
//Segmentation
var seeds = ee.Algorithms.Image.Segmentation.seedGrid(40);
// Run SNIC on the regular square grid.
var snic = ee.Algorithms.Image.Segmentation.SNIC({
  image: img,
  size: 32,
  compactness: 0.1,
  connectivity: 4,
  neighborhoodSize:256,
  seeds: seeds
}).select(['ds10111040da_mean', 'ds10111040df_mean', 'clusters'], ['ds10111040da', 'ds10111040df', 'clusters']);
var clusters = snic.select('clusters')//.reproject({crs: 'EPSG:4326', scale: 18});
Map.addLayer(clusters.randomVisualizer(), {}, 'clusters', false);
Map.addLayer(snic, {bands: ['ds10111040da', 'ds10111040df'], min:0, max:1, gamma: 0.8}, 'means', false);
var elevation_10111040 = elevation.addBands(clusters).reduceConnectedComponents(ee.Reducer.mean(), 'clusters', 256);
Map.addLayer(elevation_10111040, {min: 1, max: 3445,  gamma: [1.85], opacity: 1}, 'elevation_10111040');
// Export.image.toDrive({
//   image: elevation_10111040,
//   description: "elevation_10111040",
//   folder: "elevation_10111040",
//   scale: 2.5,
//   region: clip,
//   maxPixels:1e13,
// });

Related

Rendering imageData to new canvas

I'm following a tutorial by George Francis in the tutorial after some initial examples he shows how to use image data to create random layouts.
I'm trying to work out how to get the image data from a canvas created using paper.js, as I need to get the rgb values from each individual pixel on the canvas
Link to codepen
Unknowns:
Do I need to use the rasterize() method on the shape I've created?
Currently I am attempting the following:
// create a white rectangle the size of the view (not sure I need this but doing it so that there are both white and black pixels)
const bg = new paper.Path.Rectangle({
position: [0,0],
size: view.viewSize.multiply(2),
fillColor: 'white'
})
// create a black rectangle smaller than the view size
const shape = new paper.Path.RegularPolygon({
radius: view.viewSize.width * 0.4,
fillColor: 'black',
strokeColor: 'black',
sides: 4,
position: view.center
})
// So far so good shapes render as expected. Next put the shapes in a group
const group = new paper.Group([bg,shape])
// rasterise the group (thinking it needs to be rasterized to get the pixel data, but again , not sure?)
group.rasterize()
// iterate over each pixel on the canvas and get the image data
for(let x = 0; x < width; x++){
for(let y = 0; y < height; y++){
const { data } = view.context.getImageData(x,y,1,1)
console.log(data)
}
}
Expecting: To get an array of buffers where if the pixel is white it would give me
Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
byteLength: 4, byteOffset: 0, length: 4]
0: 255
1: 255
2: 255
//(not sure if the fourth index represents (rgb'a')?
3: 255
buffer:
ArrayBuffer(4)
byteLength: 4
byteOffset: 0
length: 4
Symbol(Symbol.toStringTag): (...)
[[Prototype]]: TypedArray
and if the pixel is black I should get
Uint8ClampedArray(4) [0, 0, 0, 0, buffer: ArrayBuffer(4),
byteLength: 4, byteOffset: 0, length: 4]
0: 0
1: 0
2: 0
3: 0
buffer:
ArrayBuffer(4)
byteLength: 4
byteOffset: 0
length: 4
Symbol(Symbol.toStringTag): (...)
[[Prototype]]: TypedArray
i.e either 255,255,255 (white) or 0,0,0(black)
Instead, all the values are 0,0,0?
I think that your issue was that at the time where you are getting the image data, your scene is not yet drawn to the canvas.
In order to make sure it's drawn, you just need to call view.update().
Here's a simple sketch demonstrating how it could be used.
Note that you don't need to rasterize your scene if you are using the Canvas API directly to manipulate the image data. But you could also rasterize it and take advantage of Paper.js helper methods like raster.getPixel().
// Draw a white background (you effectively need it otherwise your default
// pixels will be black).
new Path.Rectangle({
rectangle: view.bounds,
fillColor: 'white'
});
// Draw a black rectangle covering most of the canvas.
new Path.Rectangle({
rectangle: view.bounds.scale(0.9),
fillColor: 'black'
});
// Make sure that the scene is drawn into the canvas.
view.update();
// Get the canvas image data.
const { width, height } = view.element;
const imageData = view.context.getImageData(0, 0, width, height);
// Loop over each pixel and store all the different colors to check that this works.
const colors = new Set();
const length = imageData.data.length;
for (let i = 0; i < length; i += 4) {
const [r, g, b, a] = imageData.data.slice(i, i + 4);
const color = JSON.stringify({ r, g, b, a });
colors.add(color);
}
console.log('colors', [...colors]);

How to display individual images by each date in google earth engine?

I am new to google earth engine and not so familiar with javascript. I want to display the cleared images (B4,B3,B2 bands) of Sentinel 2 by each dates in layers (each layer represent each date). The code is shown as below, but always get error 'no Band 4, constant band'. Can anyone help me to solve this problem? Thanks!
var lakes=table.geometry();
Map.centerObject(lakes, 15);
function maskS2clouds(image) {
var qa = image.select('QA60');
// Bits 10 and 11 are clouds and cirrus, respectively.
var cloudBitMask = 1 << 10;
var cirrusBitMask = 1 << 11;
// Both flags should be set to zero, indicating clear conditions.
var mask = qa.bitwiseAnd(cloudBitMask).eq(0)
.and(qa.bitwiseAnd(cirrusBitMask).eq(0));
return image.updateMask(mask).divide(10000);
}
var start = ee.Date('2015-06-20');
var finish = ee.Date('2018-06-01');
var collection = ee.ImageCollection('COPERNICUS/S2')
.filterDate(start, finish)
.filterBounds(lakes)
.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 10))
.map(maskS2clouds);
var rgbVis = {
min: 0.0,
max: 0.3,
bands: ['B4', 'B3', 'B2'],
};
function addImage(imageL) { // display each image in collection
var id = imageL.id;
var image = ee.Image(imageL.id);
Map.addLayer(image.select(['B4','B3','B2']).clip(lakes),rgbVis,id)
}
collection.evaluate(function(collection) { // use map on client-side
print(collection.features);
collection.features.map(addImage);
})

Multidimension Bubble Chart using DC.js

I am trying to create a bubble chart using DC.js
The sample data I have is:
State_Name Area Cities Villages Population
A 200 60 1050 10000
B 300 80 1100 15678
C 500 20 2220 97767
D 600 90 3400 50000
So, I want to show total three Bubbles on a bubbleChart. The first bubble should represent total Area and rest two should represent total Cities and total Villages respectively. I have made a barChart for States_Names vs Population where xAxis represent States_name and yAxis represent Population. Now I want whenever I click on one or more bars or you can say States_name I should get the sum of Area, Cities and Villages for all those selected States_name. These sum values for Area, Cities and Villages will represent three bubbles on bubbleChart respectively.
For example: if I click on two bars from barChart which represent State_Name = A and B respectively, I
should get three bubbles on bubbleChart. On hovering these three
bubbles I should get values total Area = 200+300= 500, Total
Cities = 60+80 = 140, total Villages = 1050+1100 = 2150 respectively.
For Positioning of bubbles:
I can fix their yAxis position. And, their xAxis position and radius should depend on the value that bubble has. For above example radius of bubble representing total Villages should be larger than the radius of other two bubbles. Similarly, for xAxis position bubble representing total Cities will be on left side of other two bubbles.
I tried code from here: http://jsfiddle.net/gordonwoodhull/37uET/6/
but getting the error: "group.top is not a function". It seems
that code works only for rowChart.
var data = [
{type: 'foo', a: 10, b: 9, c: 11, d: 2},
{type: 'bar', a: 1, b: 4, c: 2, d: 3},
{type: 'foo', a: 0, b: 2, c: 1, d: 22},
{type: 'bar', a: 11, b: 5, c: 6, d: 5}];
var ndx1 = crossfilter(data);
function regroup(dim, cols) {
var _groupAll = dim.groupAll().reduce(
function(p, v) { // add
cols.forEach(function(c) {
p[c] += v[c];
});
return p;
},
function(p, v) { // remove
cols.forEach(function(c) {
p[c] -= v[c];
});
return p;
},
function() { // init
var p = {};
cols.forEach(function(c) {
p[c] = 0;
});
return p;
});
return {
all: function() {
// or _.pairs, anything to turn the object into an array
return d3.map(_groupAll.value()).entries();
}
};
}
var dim = ndx1.dimension(function(r) { return r.a; });
var sidewaysGroup = regroup(dim, ['a', 'b', 'c', 'd']);
var typeDim = ndx.dimension(function(r) { return r.type; });
var sidewaysRow = dc.bubbleChart('#state-donations')
.width(350).height(300)
.dimension(dim)
.group(sidewaysGroup)
.elasticX(true)
.maxBubbleRelativeSize(0.3)
.x(d3.scale.linear().domain([0, 10]))
.y(d3.scale.linear().domain([0, 10]))
.r(d3.scale.linear().domain([0, 400]))
.elasticY(true)
.yAxisPadding(100)
.elasticX(true)
.xAxisPadding(500);
please help!

Draw (inverted) geometric circle in google maps api v3

I'm looking for a way to draw an inverted geometric (not geographical) circle in google maps api v3.
Essentially the goal is to dim the map except around a map object - as a way to make the map object stand out. To do this, I have employed an inverted overlay and have a method to create the circle "hole" in my "shadow-overlay".
However the method I've employed to get the lat/lng coordinates to generate this circle is adjusted to the Mercator projection and is not a consistent size or shape because it is relative to it's position from the equator. The method needs to create a circle (without using google's circle object - or using it with a way to extract it's path) that will calculate the lat/lng points from a center, based on a radius field that doesn't take the Mercator projection into account - such that it will display a perfect circle anywhere it is drawn on the map.
It shouldnt be hard, but I'm struggling to convert this function to NOT apply the Mercator projection into the result:
function getCircleCoords(point, radius) {
var d2r = Math.PI / 180; // degrees to radians
var points = 90;
var circleLatLngs = new Array();
var circleLat = radius * 0.621371192 * 0.014483;
var circleLng = circleLat / Math.cos( point.lat() * d2r);
for (var i = 0; i < points+1; i++) {
var theta = Math.PI * (i / (points / 2));
var vertexLat = point.lat() + (circleLat * Math.sin(theta));
var vertexLng = point.lng() + (circleLng * Math.cos(theta));
var vertexLat = point.lat() + (circleLat * (theta));
var vertexLng = point.lng() + (circleLng * (theta));
var vertextLatLng = new google.maps.LatLng(vertexLat, vertexLng);
circleLatLngs.push( vertextLatLng );
}
return circleLatLngs;
}
This would then get called like:
feature = new google.maps.Polygon({
paths: [[my_shadow_layer_path],[getCircleCoords(latLng_, 800)] ],
fillColor: '#ff0000',
fillOpacity: 0.5,
map: map_
});
}
Thoughts?
To use a Polygon, you need to calculate the circle coordinates in pixel (world) coordinates translate them to geographic coordinates using fromPointToLatLng to get the appropriate coordinates for the circle. Or use an overlay of the correct size and handle zoom changes yourself.
You can try this library https://github.com/raihan2006i/google-map-inverted-circle. which works on Google Map V3. See below for example code blocks
var iCircle;
var map;
function initialize() {
var mapDiv = document.getElementById('gMap');
map = new google.maps.Map(mapDiv, {
center: new google.maps.LatLng(37.4419, -122.1419),
zoom: 13,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
iCircle = new InvertedCircle({
center: map.getCenter(),
map: map,
radius: 5000, // 5 km
editable: true,
stroke_weight: 5,
fill_opacity: 0.5,
fill_color: "#ff0000"
});
}
google.maps.event.addDomListener(window, 'load', initialize);

Draw a circle with google maps api3 that doesn't resize

With google maps api2 I was drawing a circle using this code:
var markerPoint = currentMarker.getPoint();
var polyPoints = Array();
var mapNormalProj = G_NORMAL_MAP.getProjection();
var mapZoom = map.getZoom();
var clickedPixel = mapNormalProj.fromLatLngToPixel(markerPoint, mapZoom);
var polySmallRadius = 20;
var polyNumSides = 20;
var polySideLength = 18;
for (var a = 0; a<(polyNumSides+1); a++) {
var aRad = polySideLength*a*(Math.PI/180);
var polyRadius = polySmallRadius;
var pixelX = clickedPixel.x + 5 + polyRadius * Math.cos(aRad);
var pixelY = clickedPixel.y - 10 + polyRadius * Math.sin(aRad);
var polyPixel = new GPoint(pixelX,pixelY);
var polyPoint = mapNormalProj.fromPixelToLatLng(polyPixel,mapZoom);
polyPoints.push(polyPoint);
}
// Using GPolygon(points, strokeColor?, strokeWeight?, strokeOpacity?, fillColor?, fillOpacity?)
highlightCircle = new GPolygon(polyPoints,"#000000",2,0.0,"#FF0000",.5);
map.addOverlay(highlightCircle);
I've managed to transform this code to api3:
var markerPoint = currentMarker.getPosition();
var polyPoints = Array();
var mapNormalProj = map.getProjection();
var mapZoom = map.getZoom();
var clickedPixel = mapNormalProj.fromLatLngToPoint(markerPoint);
var polyRadius = 20;
var polyNumSides = 20;
var polySideLength = 18;
for (var a = 0; a<(polyNumSides+1); a++) {
var aRad = polySideLength*a*(Math.PI/180);
var pixelX = clickedPixel.x + 5 + (polyRadius * Math.cos(aRad));
var pixelY = clickedPixel.y - 10 + (polyRadius * Math.sin(aRad));
var polyPixel = new google.maps.Point(pixelX,pixelY);
var polyPoint = mapNormalProj.fromPointToLatLng(polyPixel);
polyPoints.push(polyPoint);
}
highlightCircle = new google.maps.Polygon({
paths: polyPoints,
strokeColor: "#FF0000",
strokeOpacity: 0.8,
strokeWeight: 2,
fillColor: "#FF0000",
fillOpacity: 0.35
});
highlightCircle.setMap(map);
If you look more closely at the api3 example, the mapZoom variable is not used anywhere.
In api2, the code generates a small circle around my marker - around 35px radius. When I zoom into the map, the radius stays at 35px (because the zoom is taken into account).
With api3 on the other hand, I have a huge circle - more than 200px wide and when I zoom in, the circle becomes bigger and bigger.
It behaves the same way as the circle object available in api3.
What I want is just a small circle around my marker, that is not 100km in diameter, but just a few pixels around my marker (this circle acts like a hover element in html).
Any ideas how to achieve that?
You might have better luck using custom marker, not a circle. See "Vector Icon" from the documentation here: https://developers.google.com/maps/documentation/javascript/overlays#Icons
var marker = new google.maps.Marker({
position: new google.maps.LatLng(-25.363882, 131.044922),
icon: {
path: google.maps.SymbolPath.CIRCLE,
scale: 10
},
draggable: true,
map: map
});
You're doing the calculation based on the Point plane which plane remains the same no matter what zoom level you are at. You probably mean to do the calculation using pixels.
The methods you are looking for are here. fromLatLngToContainerPixel and fromContainerPixelToLatLng or fromLatLngToDivPixel and fromDivPixelToLatLng.
This means you should probably wrap up that code into an OverlayView and call getProjection() on your map to get the projection and then use one set of those methods to do the calculation.

Resources