Google Earth Engine (GEE) urban impervious surface extraction, NDBI

        First show the effect:

1. Data import

        At 10-year intervals, taking into account the operating time and model of Landsat satellites, 1986, 1995, 2005, 2015, and 2022 are set as sampling years to study the changes in the built-up areas of the Guangdong-Hong Kong-Macao Greater Bay Area from 1986 to 2022.

        For the Landsat data of all years, we use the surface reflectance data, and filter according to the cloud amount, and use the median function to remove the cloud, because it is not the focus of this experiment. For details, please refer to this blog I wrote: 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. Calculate NDBI

        This experiment is lazy and does not need to consider the very accurate extraction effect. The impermeable surface can be extracted by using NDBI (building index), NDBI = (SWIR - NIR) / (SWIR + NIR). Based on multiple attempts, set the NDBI threshold to: -0.15. Some key steps are placed below. The bands at the beginning of SR are the bands of surface reflectance data, for example, SR_B5 is the fifth band of surface reflectance. Note that the formulas calculated by Landsat5 and Landsat8 are different. The normalizedDifference function is used to calculate the various normalized indices, and you will also use it when calculating NDVI.

        Explain the code: first extract the impermeable surface in 2022, and make a mask for other years. If you don't make a mask, there may be a problem that the city's pixels change greatly every year. built5 is the result of the binarization of the impervious surface in 2022, 1 is the impervious surface, and 0 is the other. Then use the results of 2022 to make a mask for the remaining years, and then get the impermeable surface for the remaining 4 years. At the same time, it is assumed here that the impermeable surface pixels will not be converted into other pixels (corresponding to the fact that cities will not be converted into rural areas in real life), so here is a calculation for 1995, 2005, 2015 and 2022 (or function), which is combined with the results of the previous year, which actually reduces the error. The last 6 sentences are to prepare for drawing the urbanization process later. Set the 1986 impervious surface to 1, the 1995 impervious surface to 2, and so on, then set all other cells to 6. In this way, when the image collection is mined, each new impermeable surface can be obtained. Note that the values ​​of other pixels must be set to 6 and cannot be set to other values, otherwise the color will be uncertain when outputting the image. The reason for setting it to 6 is to lock the data used for outputting the image. Between the 6 consecutive integers of 1, 2, 3, 4, 5, and 6, the color can be completely determined without being linearly stretched (I checked it for a long time before and found no problem). The where function inside is actually a bit like the if function in other languages.

//-----------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 will divide part of the bare soil into urban pixels, but the problem is not very serious, after all, there is not much bare soil inside the city (Figure 1). At the same time, coastal areas and places with a lot of sediment in the sea are easy to be extracted as urban pixels, but the reduction of the threshold cannot improve the impervious surface. A better way can also use other indexes to screen the water body, which can be used as an improvement direction.

        The code for drawing the picture is as follows: first draw the main picture, that is, the first two sentences. Then add a legend. These codes can be applied. You only need to change the min and max values ​​and the colors in the palette according to your own needs, and adjust the legend name. I won’t go into details (because I don’t know much about function , basically copy other people’s, and then modify it a little bit by yourself (hehe).

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); 

             Then you can make the picture at the beginning~

             Next time we will talk about urban morphology calculations!

Guess you like

Origin blog.csdn.net/weixin_46812066/article/details/127606578