Calculating LST using Landsat 8 Level 2, Tier 1 and Landsat 8 Surface Reflectance Tier 1 gives different results? - gis

I am calculating Land surface temperature(LST) using Landsat-8 data LANDSAT/LC08/C02/T1_L2 since LANDSAT/LC08/C01/T1_SR has been deprecated. I am following the example here.
I have modified the same code but it is giving different and possibly incorrect results.
// Import country boundaries feature collection.
var dataset = ee.FeatureCollection('USDOS/LSIB_SIMPLE/2017');
// Apply filter where country name equals Uganda.
var geometry = dataset.filter(ee.Filter.eq('country_na', 'Uganda'));
function applyScaleFactors(image) {
var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);
return image.addBands(opticalBands, null, true)
.addBands(thermalBands, null, true);
}
//loading
{
var dataset = ee.ImageCollection("LANDSAT/LC08/C02/T1_L2")
.filterDate('2018-01-01','2018-12-31')
.filterBounds(geometry);
}
//applying scaling factor
dataset = dataset.map(applyScaleFactors);
var image = dataset.median();
var ndvi = image.normalizedDifference(['SR_B5',
'SR_B4']).rename('NDVI');
//selecting thermal band ST_B10
var thermal= image.select('ST_B10');
;
// find the min and max of NDVI
{
var min = ee.Number(ndvi.reduceRegion({
reducer: ee.Reducer.min(),
geometry: geometry,
scale: 30,
maxPixels: 1e9
}).values().get(0));
print(min, 'min');
var max = ee.Number(ndvi.reduceRegion({
reducer: ee.Reducer.max(),
geometry: geometry,
scale: 30,
maxPixels: 1e9
}).values().get(0));
print(max, 'max')
}
//fractional vegetation
var fv =(ndvi.subtract(min).divide(max.subtract(min))).pow(ee.Number(2)).rename('FV');
//Emissivity
var a= ee.Number(0.004);
var b= ee.Number(0.986);
var EM=fv.multiply(a).add(b).rename('EMM');
//LST in Celsius Degree bring -273.15
//NB: In Kelvin don't bring -273.15
var LST = thermal.expression(
'(Tb/(1 + (0.00115* (Tb / 1.438))*log(Ep)))-273.15', {
'Tb': thermal.select('ST_B10'),
'Ep': EM.select('EMM')
}).rename('LST');
Map.addLayer(LST, {min: 20.569706944223423, max:29.328077233404645, palette: [
'040274', '040281', '0502a3', '0502b8', '0502ce', '0502e6',
'0602ff', '235cb1', '307ef3', '269db1', '30c8e2', '32d3ef',
'3be285', '3ff38f', '86e26f', '3ae237', 'b5e22e', 'd6e21f',
'fff705', 'ffd611', 'ffb613', 'ff8b13', 'ff6e08', 'ff500d',
'ff0000', 'de0101', 'c21301', 'a71001', '911003'
]},'LST');
Result using collection1, which seems accurate,
Result using collection2 which seems incorrect,

As I understand it, collection 2 landsat has already been preprocessed so B10 is already converted into thermal. All that needs to be done is to use the following multipliers to convert it into Kelvin. Someone correct me if this is wrong?
var thermalBands = image.select('ST_B.*').multiply(0.00341802).add(149.0);

Related

Can we recall a set of variable inside the Sequence Array?

I'd like to ask about my program bcs it doesn't work correctly. I want to recall a set of variable in two different Sequence Array. Here is my code.
// Array of Arrays
var SequenceGo:Array =
\[
{dt:dt1, P:P1, s0:s01, s:s1},
{dt:dt2, P:P2, s0:s02, s:s2},
{dt:dt3, P:P3, s0:s03, s:s3},
{dt:dt4, P:P4, s0:s04, s:s4},
{dt:dt5, P:P5, s0:s05, s:s5},
{dt:dt6, P:P6, s0:s06, s:s6},
{dt:dt7, P:P7, s0:s07, s:s7},
{dt:dt8, P:P8, s0:s08, s:s8},
{dt:dt9, P:P9, s0:s09, s:s9},
{dt:dt10, P:P10, s0:s010, s:s10},
\];
var SequenceBack:Array =
\[
{dtback:dt10back, P:P10, s0:s010, sback:s10back},
{dtback:dt9back, P:P9, s0:s09, sback:s9back},
{dtback:dt8back, P:P8, s0:s08, sback:s8back},
{dtback:dt7back, P:P7, s0:s07, sback:s7back},
{dtback:dt6back, P:P6, s0:s06, sback:s6back},
{dtback:dt5back, P:P5, s0:s05, sback:s5back},
{dtback:dt4back, P:P4, s0:s04, sback:s4back},
{dtback:dt3back, P:P3, s0:s03, sback:s3back},
{dtback:dt2back, P:P2, s0:s02, sback:s2back},
{dtback:dt1back, P:P1, s0:s01, sback:s1back}
\];
function onNext(index:int = 0):void
{
if (index >= SequenceGo.length)
{
return;
}
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = SequenceBack[index];
//variables
F = s_teganganst.value;
m = s_masjenst.value/10000;
v = Math.sqrt(F/m);
tp = 5000/v;
f = s_frekuensist.value;
w = 2*Math.PI*f;
aDataGo.dt += t;
aDataGo.s = aDataGo.s0 - A * Math.sin(w * aDataGo.dt);
aDataGo.P.y = aDataGo.s;
if(P10.y < 607){
aDataBack.dtback += t;
aDataBack.sback = - A * Math.sin(w * aDataBack.dtBack);
aDataBack.P.y = aDataGo.s + aDataBack.sback;
}
setTimeout(onNext, tp, index + 1);
}
Actually, code
aDataBack.P.y = aDataGo.s + aDataBack.sback;
is not a fit code for the animation because aDataBack is ordered inversely from aDataGo (we have to stay this inverse order for the proper animation in my program). I want to recall the variables based on its number, so each variable will match with another variable. For example,
P1.y = s1 + s1back;
P2.y = s2 + s2back;
P3.y = s3 + s3back;
P4.y = s4 + s4back;
//and so on
I've tried the code above, but it also doesn't work. Any other expression for calling some couples of variables just like my code above? Thanks!
I want to recall the variables based on its number, so each variable will match with another variable
Ok, there are two options.
Option one, simple and straightforward: compose a method to find the correspondent back object on spot:
function findBack(P:Object):Object
{
for each (var aDataBack:Object in SequenceBack)
{
if (aDataBack.P == P)
{
return aDataBack;
}
}
}
So, that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = findBack(aDataGo.P);
The possible problem here is the performance. It is fine on the scale of 10 or 100 objects, but as (I suppose) you devise a particle system, the object count easily scales to thousands, and the amount of loop-searching might become cumbersome.
So I advise to prepare a pre-indexed hash so that you won't need to search each single time.
var SequenceBack:Array =
[
// ...
];
// Dictionary is a storage of key:value data, just like Object,
// but Dictionary allows Object keys.
var HashBack:Dictionary = new Dictionary;
for each (var aDataBack:Object in SequenceBack)
{
HashBack[aDataBack.P] = aDataBack;
}
I encourage you to read more about Dictionary class.
And so that piece of code would be
var aDataGo:Object = SequenceGo[index];
var aDataBack:Object = HashBack[aDataGo.P];

node js mysql query result rendered to chart.js labels in pug page

I've this strange behaviour when i make a get request. A query to mysql calls for totals of sells(float) group by days (nvarchar). I've made 2 arrays (for totals and datas) where i push the content of the result
router.get('/movmensili', function(req, res ,next){
if(!req.session.user){
return res.redirect('/');
}
executeQuery("SELECT SUM(price) as Totale, Data FROM db10101.10101 group by Data order
by Data", function(error, resmov){
var dateArray = [];
var totaliArray = [] ;
for (var i = 0; i<resmov.length; i++) {
dateArray.push(resmov[i].Data)
}
for (var i = 0; i<resmov.length; i++) {
totaliArray.push(resmov[i].Totale)
}
res.render('movmensili', {title: 'movs', date: (dateArray), totali: totaliArray
});
});
});
console.log(dateArray); //['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05']
console.log(totaliArray); //[ '4.00', '5.50', '3.00', '1.75', null ]
so far so good
once I open my Pug page i got to draw a bar chart with Chart.js
the two arrays used for the chart axes, contains numeric values, no problems for the sell totals, but the xlabels should be strings. So far the xlabes are 2016(=2022 minus 05 minus 01), 2015, 2014 and so on....
canvas#myChart(style='width: 100%; height: 100%; margin: 10 auto')
script.
const xlabels = [#{date}] //[2022-05-01,2022-05-02,2022-05-03,2022-05-04,2022-05-05]
const ydatas = [#{totali}] //[4.00,5.50,3.00,1.75,]
I wasn't able to convert / cast / stringify the x values to get the result needed.
Any suggestions?
David, this worked for me. Are you sure you are passing the labels correctly on render (try date: dateArray instead of date: (dateArray)). I didn't create a render function for this page so hard coded the labels and data arrays:
script.
var labels = ['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05',]
var data = {
labels: labels,
datasets: [{
label: 'My First dataset',
backgroundColor: 'rgb(255, 99, 132)',
borderColor: 'rgb(255, 99, 132)',
data: ['4.00', '5.50', '3.00', '1.75', null],
}]
};
var config = {type: 'line',data: data,options: {}};
var myChart = new Chart(document.getElementById("myChart"),config);
Not best solution, but it works....
async function GetData()
var xlabel = '#{date}';
var xlabel = xlabel.replace(/"/g, '"');
alert(xlabel);
var xlabel = xlabel.split(",");
alert(xlabel);
for(i = 0; i < xlabel.length; i += 1){
xlabel[i] = xlabel[i];
//alert(numarray[i]);
xlabels.push(xlabel[i]);
}
if I hardcode the labels in:
const xlabels = [#{date}]
instead of importing from page render, everything works fine. It's exactly that the point. The console.log of dateArray is perfectly as I would like to be in the xlabels, while once imported the quotes disappear
console.log(dateArray); // ['2022-05-01','2022-05-02','2022-05-03','2022-05-04','2022-05-05']
const xlabels = [#{date}]; // [2022-05-01,2022-05-02,2022-05-03,2022-05-04,2022-05-05]

How to access an object in AS3

I wrote this code
var enemies:Object = new Object();
// HP MP ATK DEF MATK MDEF AGI LUCK
enemies.Goblin = [40, 20, 6, 6, 3, 3, 4, 1];
which contains those stats for the goblin and I created a function that should take the stats from enemies.Goblin and put them in some variables but it won't work.
function createEnemy(enemyName:String):void {
e_hp = enemies.enemyName[0];
e_mp = enemies.enemyName[1];
e_atk = enemies.enemyName[2];
e_def = enemies.enemyName[3];
e_matk = enemies.enemyName[4];
e_mdef = enemies.enemyName[5];
e_agi = enemies.enemyName[6];
e_luck = enemies.enemyName[7];
}
This is the output error when the createEnemy function is executed: TypeError: Error #1010: A term is undefined and has no properties.
Object "enemies" does not have "enemyName" property.
Try this:
enemies[enemyName][0]
enemies[enemyName][1]
...
The answer had been given but what are you doing is a wrong way to do. Accessing properties by index is asking for trouble in a very near future.
It is better to do with classes but since you're using objects, I will try use objects too:
var goblin_stats:Object = { hp:40, mp:20, atk:6, def:6 }; // and so on
var elf_stats:Object = { hp:35, mp:30, atk:8, def:4 }; // and so on
...
// add as much characters as needed
Now I believe you just want to create a fresh goblin based on goblin stats. Just pass the stats to the createEnemy function:
createEnemy(goblin_stats);
function createEnemy(stats:Object):void {
e_hp = stats.hp;
e_mp = stats.mp;
// and so on
}
or better:
function createEnemy(stats:Object):void {
for (var property:String in stats) e_stats[property] = stats[property];
}
Store objects (everything) in arrays for easy referencing. Here are the key code:
var aEnemies:Array = new Array();
var mcEnemy:Object = new Object();
mcEnemy.iHP = 40; // set iHP property to 40
aEnemies.push(mcEnemy); // add enemy to array of enemies
trace("enemy 0's HP: " + aEnemies[0].iHP);

actionscript arrays merge

I posted my problem a few hours ago, but I think I figured out how to ask my question in a more comprehensible way.
This is my code:
// 1. Intro
var introPL1:Array = ["intro1","intro2","intro3","intro4"];
var introPL2:Array = ["intro5","intro6","intro7","intro8","intro9"];
var introPL3:Array = ["intro10","intro11"];
var introPL4:Array = ["intro12","intro13"];
var allIntro:Array = [introPL1,introPL2,introPL3,introPL4];
// 2. Clothes
var clothesPL1:Array = ["clothes1","clothes2","clothes3","clothes4","clothes5"];
var clothesPL2:Array = ["clothes6","clothes7","clothes8"];
var clothesPL3:Array = ["clothes9","clothes10"];
var clothesPL4:Array = ["clothes11","clothes12","clothes13"];
var allClothes:Array = [clothesPL1,clothesPL2,clothesPL3,clothesPL4];
// 3. Colored Numbers
var colNumPL1:Array = ["colNum1","colNum2","colNum3","colNum4","colNum5"];
var colNumPL2:Array = ["colNum6","colNum7","colNum8"];
var colNumPL3:Array = ["colNum9","colNum10"];
var colNumPL4:Array = ["colNum11","colNum12","colNum13"];
var allColNum:Array = [colNumPL1,colNumPL2,colNumPL3,colNumPL4];
var allStuff:Array;
allStuff = allIntro.concat(allClothes, allColNum);
trace(allStuff[4]);
When I trace allStuff[4] it displays "clothes1,clothes2,clothes3,clothes4,clothes5".
The thing is, I would like all the stuff to be in the allStuff array (without sub-arrays) and when I trace allStuff[4], I would like it to display "intro5" (the fifth item in the huge allStuff array).
the function you want to use then is concat
here's the example from adobe
var numbers:Array = new Array(1, 2, 3);
var letters:Array = new Array("a", "b", "c");
var numbersAndLetters:Array = numbers.concat(letters);
var lettersAndNumbers:Array = letters.concat(numbers);
trace(numbers); // 1,2,3
trace(letters); // a,b,c
trace(numbersAndLetters); // 1,2,3,a,b,c
trace(lettersAndNumbers); // a,b,c,1,2,3
it's pretty straight forward:
allStuff= allStuff.concat(introPL1,introPL2,introPL3,introPL4,clothesPL1,clothesPL2,clothesPL3,clothesPL4,colNumPL1,colNumPL2,colNumPL3,colNumPL4);
you could also do a
allStuff = []
for each(var $string:String in $arr){
allStuff.push($string)
}
for each array, or make it into a function
Okay, once you have declared your arrays like so, you need an additional operation to flatten your arrays allClothes and so on. Do like this:
function flatten(a:Array):Array {
// returns an array that contains all the elements
// of parameter as a single array
var b:Array=[];
for (var i:int=0;i<a.length;i++) {
if (a[i] is Array) b=b.concat(flatten(a[i]));
else b.push(a[i]);
}
return b;
}
What does it do: The function makes an empty array first, then checks the parameter member by member, if the i'th member is an Array, it calls itself with that member as a parameter, and adds the result to its temporary array, otherwise it's just pushing next member of a into the temporary array. So, to make your allIntro a flat array, you call allIntro=flatten(allIntro) after declaring it as you did. The same for other arrays.

Google maps - how to get building's polygon coordinates from address?

How to implement the following:
User defines an address
User defines a color
Service searches for a corresponding building on the google map
Service fills the found building on the map with the color
I know how to:
1.find lat/long of the address
2.draw the polygon
So, to do the task I need to get polygon coordinates of building from address. How to?
(1) Acquire image tile
(2) Segment buildings based on pixel color (here, 0xF2EEE6).
(3) Image cleanup (e.g. erosion then dilation) + algorithm to acquire pixel coordinates of polygon corners.
(4) Mercator projection to acquire lat/long of pixel
You can convert the address to geographic coordinates by the use of the Google Geocoding API.
https://maps.googleapis.com/maps/api/geocode/json?address=SOME_ADDRESS&key=YOUR_API_KEY
Then, you can use Python and a styled static map to obtain the polygon of the building (in pixel coordinates) at some location:
import numpy as np
from requests.utils import quote
from skimage.measure import find_contours, points_in_poly, approximate_polygon
from skimage import io
from skimage import color
from threading import Thread
center_latitude = None ##put latitude here
center_longitude = None ##put longitude here
mapZoom = str(20)
midX = 300
midY = 300
# Styled google maps url showing only the buildings
safeURL_Style = quote('feature:landscape.man_made|element:geometry.stroke|visibility:on|color:0xffffff|weight:1')
urlBuildings = "http://maps.googleapis.com/maps/api/staticmap?center=" + str_Center + "&zoom=" + mapZoom + "&format=png32&sensor=false&size=" + str_Size + "&maptype=roadmap&style=visibility:off&style=" + safeURL_Style
mainBuilding = None
imgBuildings = io.imread(urlBuildings)
gray_imgBuildings = color.rgb2gray(imgBuildings)
# will create inverted binary image
binary_imageBuildings = np.where(gray_imgBuildings > np.mean(gray_imgBuildings), 0.0, 1.0)
contoursBuildings = find_contours(binary_imageBuildings, 0.1)
for n, contourBuilding in enumerate(contoursBuildings):
if (contourBuilding[0, 1] == contourBuilding[-1, 1]) and (contourBuilding[0, 0] == contourBuilding[-1, 0]):
# check if it is inside any other polygon, so this will remove any additional elements
isInside = False
skipPoly = False
for othersPolygon in contoursBuildings:
isInside = points_in_poly(contourBuilding, othersPolygon)
if all(isInside):
skipPoly = True
break
if skipPoly == False:
center_inside = points_in_poly(np.array([[midX, midY]]), contourBuilding)
if center_inside:
# approximate will generalize the polygon
mainBuilding = approximate_polygon(contourBuilding, tolerance=2)
print(mainBuilding)
Now, you can convert the pixel coordinates to latitude and longitude by the use of little JavaScript, and the Google Maps API:
function point2LatLng(point, map) {
var topRight = map.getProjection().fromLatLngToPoint(map.getBounds().getNorthEast());
var bottomLeft = map.getProjection().fromLatLngToPoint(map.getBounds().getSouthWest());
var scale = Math.pow(2, map.getZoom());
var worldPoint = new google.maps.Point(point.x / scale + bottomLeft.x, point.y / scale + topRight.y);
return map.getProjection().fromPointToLatLng(worldPoint);
}
var convertedPointsMain = [];
for (var i = 0; i < pxlMainPolygons[p].length; i++) {
var conv_point = {
x: Math.round(pxlMainPolygons[p][i][1]),
y: Math.round(pxlMainPolygons[p][i][0])
};
convertedPointsMain[i] = point2LatLng(conv_point, map);
}
console.log(convertedPointsMain);
Might I humbly suggest you use OpenStreetMaps for this instead ?
It's a lot easier, because then you can use the OverPass API.
However, polygons might not match with google-maps or with state survey.
The latter also holds true if you would use google-maps.
// https://wiki.openstreetmap.org/wiki/Overpass_API/Overpass_QL
private static string GetOqlBuildingQuery(int distance, decimal latitude, decimal longitude)
{
System.Globalization.NumberFormatInfo nfi = new System.Globalization.NumberFormatInfo()
{
NumberGroupSeparator = "",
NumberDecimalSeparator = ".",
CurrencyGroupSeparator = "",
CurrencyDecimalSeparator = ".",
CurrencySymbol = ""
};
// [out: json];
// way(around:25, 47.360867, 8.534703)["building"];
// out ids geom meta;
string oqlQuery = #"[out:json];
way(around:" + distance.ToString(nfi) + ", "
+ latitude.ToString(nfi) + ", " + longitude.ToString(nfi)
+ #")[""building""];
out ids geom;"; // ohne meta - ist minimal
return oqlQuery;
}
public static System.Collections.Generic.List<Wgs84Point> GetWgs84PolygonPoints(int distance, decimal latitude, decimal longitude)
{
string[] overpass_services = new string[] {
"http://overpass.osm.ch/api/interpreter",
"http://overpass.openstreetmap.fr/api/interpreter",
"http://overpass-api.de/api/interpreter",
"http://overpass.osm.rambler.ru/cgi/interpreter",
// "https://overpass.osm.vi-di.fr/api/interpreter", // offline...
};
// string url = "http://overpass.osm.ch/api/interpreter";
// string url = "http://overpass-api.de/api/interpreter";
string url = overpass_services[s_rnd.Next(0, overpass_services.Length)];
System.Collections.Specialized.NameValueCollection reqparm = new System.Collections.Specialized.NameValueCollection();
reqparm.Add("data", GetOqlBuildingQuery(distance, latitude, longitude));
string resp = PostRequest(url, reqparm);
// System.IO.File.WriteAllText(#"D:\username\Documents\visual studio 2017\Projects\TestPlotly\TestSpatial\testResponse.json", resp, System.Text.Encoding.UTF8);
// System.Console.WriteLine(resp);
// string resp = System.IO.File.ReadAllText(#"D:\username\Documents\visual studio 2017\Projects\TestPlotly\TestSpatial\testResponse.json", System.Text.Encoding.UTF8);
System.Collections.Generic.List<Wgs84Point> ls = null;
Overpass.Building.BuildingInfo ro = Overpass.Building.BuildingInfo.FromJson(resp);
if (ro != null && ro.Elements != null && ro.Elements.Count > 0 && ro.Elements[0].Geometry != null)
{
ls = new System.Collections.Generic.List<Wgs84Point>();
for (int i = 0; i < ro.Elements[0].Geometry.Count; ++i)
{
ls.Add(new Wgs84Point(ro.Elements[0].Geometry[i].Latitude, ro.Elements[0].Geometry[i].Longitude, i));
} // Next i
} // End if (ro != null && ro.Elements != null && ro.Elements.Count > 0 && ro.Elements[0].Geometry != null)
return ls;
} // End Function GetWgs84Points
I've been working on this for hours, the closest I have come is finding a request uri that returns a result with a polygon in it. I believe it specifies the building(boundary) by editids parameter. We just need a way to get the current editids from a building(boundary).
The URI I have is:
https://www.google.com/mapmaker?hl=en&gw=40&output=jsonp&ll=38.934911%2C-92.329359&spn=0.016288%2C0.056477&z=14&mpnum=0&vpid=1354239392511&editids=nAlkfrzSpBMuVg-hSJ&xauth=YOUR_XAUTH_HERE&geowiki_client=mapmaker&hl=en
Part of the result has what is needed:
"polygon":[{"gnew":{"loop":[{"vertex":[{"lat_e7":389364691,"lng_e7":-923341133},{"lat_e7":389362067,"lng_e7":-923342783},{"lat_e7":389361075,"lng_e7":-923343356},{"lat_e7":389360594,"lng_e7":-923342477},
I was intrigued on this problem and wrote a solution to it. See my github project.
The Google Maps API contains a GeocoderResults object that might be what you need. Specifically the data returned in the geometry field.