草庐IT

Google Earth Engine(GEE)城市不透水面提取,NDBI

我要当太空人!爸爸妈妈可高兴了 2024-03-15 原文

        先展示做出来的效果:

1.数据导入

        以10年为间隔,同时考虑Landsat卫星的运行时间和型号,设置1986、1995、2005、2015和2022为采样年份,研究1986-2022年粤港澳大湾区的建成区的变化。

        对于所有年份的Landsat数据,我们都使用地表反射率数据,并根据云量进行筛选,使用median函数进行去云,因为不是本次实验的重点,具体细节可以参考我写的这篇博客:http://t.csdn.cn/QeD1k。

var first_year = 1986;
var second_year =1995;
var third_year =2005;
var fourth_year = 2015;
var fifth_year =2022;
//---------------------------Part 2 load study area and data-------------------------------------------------------
//load the study area
var roi = table;
Map.addLayer(roi, {}, 'roi',false);
Map.centerObject(roi,8);

// Applies scaling factors landsat 5.
function applyScaleFactors5(image) {
  var opticalBands = image.select('SR_B.').multiply(0.0000275).add(-0.2);
  var thermalBand = image.select('ST_B6').multiply(0.00341802).add(149.0);
  return image.addBands(opticalBands, null, true)
              .addBands(thermalBand, null, true);
}

// Applies scaling factors for landsat 8.
function applyScaleFactors8(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);
}

var landsat1 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
    .filterBounds(roi)
    .filterDate('1986-01-01', '1986-12-28')
    .map(applyScaleFactors5)
    .filter(ee.Filter.lt('CLOUD_COVER',12))
    .median();

var landsat2 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
    .filterBounds(roi)
    .filterDate('1995-01-01', '1995-12-31')
    .map(applyScaleFactors5)
    .sort('CLOUD_COVER')
    .filter(ee.Filter.lt('CLOUD_COVER',12))
    .median();
                
var landsat3 = ee.ImageCollection('LANDSAT/LT05/C02/T1_L2')
    .filterBounds(roi)
    .filterDate('2005-01-01', '2005-12-31')
    .map(applyScaleFactors5)
    .sort('CLOUD_COVER')
    .filter(ee.Filter.lt('CLOUD_COVER',5))
    .median();
                  
var landsat4 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    .filterBounds(roi)
    .filterDate('2015-01-01', '2015-12-31')
    .map(applyScaleFactors8)
    .sort('CLOUD_COVER')
    .filter(ee.Filter.lt('CLOUD_COVER',19))
    .median();

var landsat5 = ee.ImageCollection('LANDSAT/LC08/C02/T1_L2')
    .filterBounds(roi)
    .filterDate('2022-04-01', '2022-12-30')
    .map(applyScaleFactors8)
    .filter(ee.Filter.lt('CLOUD_COVER',2))
    .median();

2.计算NDBI

        本次实验比较偷懒,不需要考虑非常准确的提取效果,使用NDBI(建筑物指数)可以将不透水面提取出来,NDBI = (SWIR - NIR) / (SWIR + NIR)。基于多次尝试,设置NDBI阈值为:-0.15。下面放了一些关键步骤。其中的SR开头的波段都是地表反射率数据的波段,比如SR_B5就是地表反射率的第五波段。注意Landsat5和Landsat8计算的公式是不一样的。normalizedDifference函数就是计算归一化的各种指数的,你还会在计算NDVI的时候用到它。

        解释一下代码:首先提取2022年的不透水面,为其他年份做一个掩膜,如果不做掩膜,则可能出现每一年城市的像元变化较大的问题。built5就是2022年不透水面二值化的结果,1为不透水面,0为其他。然后剩下的年份全用2022的结果做一个mask,就得到了剩下4年的不透水面。同时,这里假设不透水面像元不会转成其他像元(对应到现实生活中就是城市不会转成乡村),所以这里对1995,2005,2015和2022年都做了一个并运算(or函数),与前一年的结果进行合并,其实这样也减少了误差。后面6句话是为了后面绘制城市化过程做准备。将1986年不透水面设置为1,1995年不透水面设置为2,以此类推,然后将全部的其他像元都设置为6。这样将影像集合取min的时候,就可以得到每一次新增的不透水面。此时注意其他像元的值一定要设成6而不能设置成别的值,否则在出图的时候就会导致颜色存在不确定性,设置成6是因为想把出图所用到的数据锁定在1,2,3,4,5,6这6个连续整数之间,颜色就可以被完全确定下来,而不会被线性拉伸了(我之前检查了好久检查不出问题)。里面那个where函数其实就有点像是其他语言里面的if函数。

//-----------Part 3 detect urban renewal----------------------------
//calculate ndbi
// print(landsat1)
var ndbi1 = landsat1.normalizedDifference(['SR_B5','SR_B4']);
var ndbi2 = landsat2.normalizedDifference(['SR_B5','SR_B4']);
var ndbi3 = landsat3.normalizedDifference(['SR_B5','SR_B4']);
var ndbi4 = landsat4.normalizedDifference(['SR_B6','SR_B5']);
var ndbi5 = landsat5.normalizedDifference(['SR_B6','SR_B5']);

//set thresholds
var ndbi_t = -0.15;

var built5 = ndbi5.gt(ndbi_t).clipToCollection(roi);
built5 = built5.mask(built5);

var built1 = ndbi1.gt(ndbi_t).mask(built5);
var built2 = ndbi2.gt(ndbi_t).mask(built5).or(built1);
var built3 = ndbi3.gt(ndbi_t).mask(built5).or(built2);
var built4 = ndbi4.gt(ndbi_t).mask(built5).or(built3);
built5 = built5.or(built4);

built1 = built1.where(built1.gt(0),1).where(built1.eq(0),9);
built2 = built2.where(built2.gt(0),2).where(built2.eq(0),9);
built3 = built3.where(built3.gt(0),3).where(built3.eq(0),9);
built4 = built4.where(built4.gt(0),4).where(built4.eq(0),9);
built5 = built5.where(built5.gt(0),5).where(built5.eq(0),9);

var collection = ee.ImageCollection([built1,built2,built3,built4,built5])

        NDBI会将部分的裸土错分为城市像元,但问题不是很严重,毕竟城市内部的裸土不多(图1)。同时沿海地区和入海口泥沙较多的地方都容易被提取成城市像元,但是缩小阈值又提不全不透水面。比较好的方式还可以用别的指数筛选一遍水体,这可以作为改进方向。

        出图的代码如下所示:首先把主图画出来,也就是前两句话。然后添加图例。这些代码都可以套用,只需要根据自己的需要改一下min和max的值和palette里面的颜色,还有调整图例的名称即可,具体的细节就不展开说了(因为我也不是很懂functio,基本都是copy别人的,然后自己小改一下嘿嘿)。

var result=collection.min();  
Map.addLayer(result,{'min':1,'max':6,'palette':['EA047E','FF6D28','FCE700','00F5FF','00C897','00000000']},'result');  
  
//----------------Add the legend!------ -----------  
//添加图例函数                           
function addLegend(palette, names) {    
//图例的底层Panel    
var legend = ui.Panel({style: {position: 'bottom-right', padding: '5px 10px'}});    
//图例标题    
var title = ui.Label({value:'城市化年份',style: {fontWeight: 'bold', color: "red", fontSize: '16px'}});    
legend.add(title);    
//添加每一列图例颜色以及说明    
var addLegendLabel = function(color, name) {    
      var showColor = ui.Label({style: {backgroundColor: '#' + color, padding: '8px', margin: '0 0 4px 0'}});    
      var desc = ui.Label({value: name, style: {margin: '0 0 4px 8px'}});    
      return ui.Panel({widgets: [showColor, desc], layout: ui.Panel.Layout.Flow('horizontal')});    
};    
//添加所有的图例列表    
for (var i = 0; i < palette.length; i++) {    
  var label = addLegendLabel(palette[i], names[i]);    
  legend.add(label);    
}      
Map.add(legend);    
}    
    
//color table & legend name    
var palette = ['EA047E','FF6D28','FCE700','00F5FF','00C897'];    
var names = ["1986","1995","2005",'2015','2022'];    
  
//添加图例     
addLegend(palette, names); 

             然后就可以做出一开始的那个图啦~

             下一次就讲城市形态学计算!

有关Google Earth Engine(GEE)城市不透水面提取,NDBI的更多相关文章

  1. ruby-on-rails - Rails - 从命名路由中提取 HTTP 动词 - 2

    Rails中有没有一种方法可以提取与路由关联的HTTP动词?例如,给定这样的路线:将“users”匹配到:“users#show”,通过:[:get,:post]我能实现这样的目标吗?users_path.respond_to?(:get)(显然#respond_to不是正确的方法)我最接近的是通过执行以下操作,但它似乎并不令人满意。Rails.application.routes.routes.named_routes["users"].constraints[:request_method]#=>/^GET$/对于上下文,我有一个设置cookie然后执行redirect_to:ba

  2. ruby-on-rails - Ruby - 如何从 ruby​​ 上的 .pfx 文件中提取公钥、rsa 私钥和 CA key - 2

    我有一个.pfx格式的证书,我需要使用ruby​​提取公共(public)、私有(private)和CA证书。使用shell我可以这样做:#ExtractPublicKey(askforpassword)opensslpkcs12-infile.pfx-outfile_public.pem-clcerts-nokeys#ExtractCertificateAuthorityKey(askforpassword)opensslpkcs12-infile.pfx-outfile_ca.pem-cacerts-nokeys#ExtractPrivateKey(askforpassword)o

  3. ruby - 如何在ruby中提取方括号内的内容 - 2

    我正在尝试提取方括号内的内容。到目前为止,我一直在使用它,它有效,但我想知道我是否可以直接在正则表达式中使用某些东西,而不是使用这个删除功能。a="Thisissuchagreatday[coolawesome]"a[/\[.*?\]/].delete('[]')#=>"coolawesome" 最佳答案 差不多。a="Thisissuchagreatday[coolawesome]"a[/\[(.*?)\]/,1]#=>"coolawesome"a[/(?"coolawesome"第一个依赖于提取组而不是完全匹配;第二个利用前瞻和

  4. 用于从 Open3.popen3 标准输出中提取值的正则表达式 - 2

    如何获取外部命令的输出并从中提取值?我有这样的东西:stdin,stdout,stderr,wait_thr=Open3.popen3("#{path}/foobar",configfile)if/exit0/=~wait_thr.value.to_srunlog.puts("Foobarexitednormally.\n")puts"Testcompleted."someoutputvalue=stdout.read("TX.*\s+(\d+)\s+")puts"Outputvalue:"+someoutputvalueend我没有在标准输出上使用正确的方法,因为Ruby告诉我它不能

  5. ruby - 使用 Ruby CSV 提取一列 - 2

    我一直在尝试从csv文件中获取单个列。我已经阅读了文档,http://www.ruby-doc.org/stdlib/libdoc/csv/rdoc/index.html但仍然不太了解如何使用它。如果我使用CSV.table,与CSV.read相比,响应速度非常慢。我承认我正在加载的数据集非常大,这正是我只想从中获取单个列的原因。我的请求目前看起来像这样@dataTable=CSV.table('path_to_csv.csv')当我调试时,我得到了的响应#ThedocumentationsaysIshouldbeabletouseby_col(),butwhenItrytooutpu

  6. ruby - 在 Ruby 中,如何从具有值的哈希中提取键 - 2

    当我写这篇文章时,我以为我是Ruby巨人:#havingthishashhash={'Portugal'=>1,'France'=>2,'USA'=>3}#country_idcomesfrominputcountry_name=(hash.select{|k,v|v==country_id.to_i}.first||[]).first它确实正确地提取了国家名称,如果找不到国家也不会失败。我对此非常满意。但是我的导师说它可以/应该在可读性、长度和性能方面进行优化!还有什么比这更清晰/更快的呢?请指教 最佳答案 嗯,看来你的导师是对的

  7. ruby-on-rails - 在 Ruby 中提取 IMG 标签 - 2

    是否可以从Ruby中的HTMLblock中提取IMG标签(或只是IMG标签的src属性)?例如,如果我有一个HTMLblock,例如:Loremipsumdolorsitamet,laboreetdoloremagnaaliqua.Duisauteiruredolorinreprehenderitinvoluptatevelitessecillumdoloreeufugiatnullapariatur.我可以通过正则表达式或其他方法只提取IMG标签或该IMG标签的src吗?提前感谢您的任何建议! 最佳答案 使用Nokogiri:re

  8. ruby - 如何从 Proc 对象中提取代码? - 2

    给定一个Proc对象,是否可以查看其中的代码?例如:p=Proc.new{test=0}我需要的是通过某种方式从已创建的Proc对象中获取字符串“test=0”。 最佳答案 您可以使用ruby2ruby图书馆:>>#testedwith1.8.7>>require"parse_tree"=>true>>require"ruby2ruby"=>true>>require"parse_tree_extensions"=>true>>p=Proc.new{test=0}>>p.to_ruby=>"proc{test=0}"您还可以将此过程

  9. ruby - 使用 OpenSSL ruby​​ 从一个 .p12 文件中提取多个 key - 2

    我想知道如何从Apple.p12文件中提取key。根据我有限的理解,.p12文件是X504证书和私钥的组合。我看到我遇到的每个.p12文件都有一个X504证书和至少一个key,在某些情况下有两个key。这是因为每个.p12都有一个Apple开发人员key,有些还有一个额外的key(可能是Appleroot授权key)。我只考虑那些具有两个key的.p12文件是有效的。我的目标是区分具有一个key的.p12文件和具有两个key的.p12文件。到目前为止,我已经使用OpenSSL来检查X504文件和任何.p12的key。例如,我有这段代码可以检查目录中的所有.p12文件:Dir.glob(

  10. ruby - 如何使用 Ruby 提取 .rar 文件? - 2

    我需要用Ruby解压一个.rar文件。不过我找不到gem。我发现了rar只允许创建存档的gem。如何提取rar文件,而不仅仅是创建它? 最佳答案 在对这个主题做了一些额外的阅读之后,似乎所有与此有关的gem基本上都被抛弃了。但是,您可以brewinstallunrar并从Rubysystem('unrarlyour_file.rar')使用它。 关于ruby-如何使用Ruby提取.rar文件?,我们在StackOverflow上找到一个类似的问题: https

随机推荐