简介

空气质量是环境健康的一个重要方面,直接影响生态系统和人类的福祉。随着空气污染的后果日益明显,监测和了解大气参数的动态已成为当务之急。通过该项目,我们旨在利用卫星数据的能力,尤其是 2017 年 10 月 13 日发射的哨兵-5P 号卫星的数据,深入了解二氧化氮(NO2)、臭氧(O3)、二氧化硫(SO2)、一氧化碳(CO)、甲醛(HCHO)、甲烷(CH4)等关键大气成分,以及云层覆盖率和气溶胶指数。

数据集

Sentinel-5P

哨兵-5 前兆任务仪器收集的数据有助于评估空气质量。TROPOMI 仪器是一个多光谱传感器,可记录对测量大气中臭氧、甲烷、甲醛、气溶胶、一氧化碳、氧化氮和二氧化硫浓度以及云层特征非常重要的波长的反射率,空间分辨率为 0.01 弧度。

https://developers.google.com/earth-engine/datasets/catalog/sentinel-5p 

 

二氧化氮(NO2):
二氧化氮是一种重要的空气污染物,主要来源于燃烧过程、工业活动和汽车尾气排放。监测二氧化氮水平对于评估人为活动对空气质量和公众健康的影响至关重要。

臭氧 (O3):
虽然高层大气中的臭氧可以吸收有害的紫外线辐射,对保护地球上的生命至关重要,但地面臭氧可能会损害人类健康。该参数是空气质量的一个关键指标,受到污染物和阳光复杂相互作用的影响。

二氧化硫 (SO2):
二氧化硫是燃烧含硫化石燃料产生的气体。监测二氧化硫水平对于跟踪工业排放和火山活动至关重要,这两者都会对环境和健康产生重大影响。

一氧化碳 (CO):
一氧化碳是一种无色无味的气体,由含碳燃料不完全燃烧产生。汽车尾气和工业生产过程会导致一氧化碳含量升高。监测一氧化碳有助于评估空气质量和潜在污染源。

甲醛 (CH2O):
甲醛是一种分子式为 CH2O、结构为 H-CHO 的有机化合物。甲醛是一种无色气体,有刺激性气味,主要来源于燃烧过程、工业活动和自然界。它在地面臭氧的形成过程中起着至关重要的作用,并导致空气质量下降。接触甲醛会对健康产生不良影响,包括刺激呼吸道和眼睛。

甲烷(CH4):
甲烷是一种强效温室气体,主要由自然过程(湿地、白蚁)和人类活动(如牲畜消化、稻田和化石燃料开采和加工)产生。虽然甲烷不会直接危害人类健康,但由于其具有很高的全球升温潜能值,因此是气候变化的重要因素。跟踪甲烷含量对气候研究和减排战略至关重要。

云层覆盖:
云在地球的能量平衡中发挥着重要作用,并影响着太阳辐射的分布。云层覆盖是气候研究中的一个关键因素,因为它影响温度、降水和整体天气模式。在卫星观测中,云层会遮挡地球表面的视线,影响测量的准确性。在空气质量监测研究中,采用了先进的算法和数据融合技术来减轻云层的影响。

气溶胶指数:
气溶胶指数(AI)是测量大气中气溶胶浓度的指标。气溶胶包括颗粒和液滴,其存在有多种来源,如污染、沙尘暴和火山爆发。气溶胶会影响空气质量、气候和人类健康。气溶胶会散射或吸收阳光,影响云的形成,吸入后还会引起呼吸道问题。

本文试图展示一个创新而简单的解决方案--利用谷歌地球引擎 (GEE) 和 Sentinel-5P 的强大功能开发的空气质量监测应用程序,该应用程序主要针对印度尼西亚。

首先要说明的是,哨兵-5P 数据有两种类型,即离线 (OFFL) 和近实时 (NRTI),指的是数据对用户的可用性和交付速度。下面是对两者区别的解释:

离线 (OFFL):
离线数据是指无法立即获得或访问的信息。就哨兵-5P 而言,离线数据通常是指卫星收集并存储以供日后使用的历史数据或存档数据。我们可以将离线数据用于回顾性研究、长期趋势分析和深入研究。这类数据不适合需要实时或接近实时信息的应用。

近实时(NRTI):
近实时数据指的是卫星采集后不久就能提供的信息。它不是瞬时数据,但从数据采集到提供给用户之间的延迟时间极短。近实时数据对于需要及时信息的应用至关重要,例如监测动态环境变化、应对自然灾害或为业务目的提供当前状态更新。近实时数据通常用于空气质量监测、灾害响应和气候研究等应用,在这些应用中,最新信息至关重要。

APP制作和数据处理过程

因此,既然我们要创建一个简单的空气质量监测应用程序,我们将使用近实时(NRTI)应用程序。下面将逐步介绍如何制作。

准备好我们要使用的资产,在本文中我使用的是北京市带县区的的 shapefile文件。您可以通过此链接访问。然后,将其定义为 prov。这里我们可以跟觉自己的需求改为其它名字,也就是district and county区县,当然不改也可以,这里只需要上传我们的二级矢量文件即可。

在此之前,我将代码分为三个部分,分别是可视化(Visualization)部分,其中包含与可视化相关的内容,如符号、图例设置等;日期配置(Date Configuration)部分,其中包含与我们的主要部件(即我们将放置在右下角的日期滑块部件)相关的设置;最后一部分是主函数(Main Function)部分,它是这个简单应用程序中将使用的主要函数,其中包含我们将使用的数据集以及与我们创建的日期滑块部件的连接。
从可视化部分开始,代码如下。首先,从北京市范围中设置我们的区域,并将其定义为区域,设置地图中心,然后创建标题小部件为 "北京市每周平均空气质量监测"。

下一步是设置符号,如调色板颜色设置、图例部件的彩条设置以及图例样式本身。我使用 Gena 提供的调色板软件包来创建彩条图例的调色板。

var pal = require('users/gena/packages:palettes');
var Jet = pal.misc.jet[7]
var BrBG = pal.colorbrewer.BrBG[10].reverse()
var Ice = pal.cmocean.Ice[7]
var Inferno = pal.matplotlib.inferno[7]

此外,如果您想添加省名标签,可以添加此代码。我在这一部分还使用了 Gena 的软件包:文本,从 shapefile 的 "provinsi "列中 "调用 "省名。但需要注意的是,添加这个标签会使数据加载时间更长。

我们必须将之前设置的所有内容组合成一个图例,并将其显示在我们创建的应用程序屏幕的左下角。

第二部分,即日期配置部分。首先,我们必须将时间从当前日期中剥离,因此为了更好地获取数据,第一次加载的数据将是前一天的数据。从该页面可以看到,可以获取的最新数据来自 Sentinel-5P 甲烷,即 2019-02-08。因此,我们只需将开始日期设置为 2019-01-01(注意:我们无法访问这两个日期之间的甲烷数据),从而采取中间方式。由于我们将显示每周数据,因此不要忘记将日期滑块 widget 中的日期周期设置为 7。 

在主函数部分,我们将创建名为 updateVisualization 的主函数。因此,当我们点击日期滑块 widget 中的某个日期时,我们将显示该日期的数据(周平均值),所以正如你所看到的,在 endDateTime 变量中,我们将其设置为周平均值,在获取数据时,不要忘记添加 .mean() 并将其剪辑为区域,以便将其集中在印度尼西亚地区。

接下来是我们创建的应用程序的显示效果。要更改要查看的数据,可以在 "图层 "部分进行切换;要更改日期,可以在屏幕右下角的日期部件中进行更改。

函数

ui.Label(valuestyletargetUrlimageUrl)

A text label.

Arguments:

value (String, optional):

The text to display. Defaults to an empty string.

style (Object, optional):

An object of allowed CSS styles with their values to be set for this widget. See style() documentation.

targetUrl (String, optional):

The url to link to. Defaults to an empty string.

imageUrl (String, optional):

Optional image url. If provided, the label will be rendered as an image and the value text will be shown on mouse hover. Only data: urls and icons loaded from gstatic.com are allowed.

Returns: ui.Label

ui.Panel.Layout.flow(directionwrap)

Returns a layout that places its widgets in a flow, either horizontal or vertical.

By default, widgets take up their natural space within a flow layout panel. Set the "stretch" style property on an added widget to stretch it to fill available space in the relevant direction:

  • horizontal, vertical, both

When multiple widgets are stretched, the available space is split equally among them. Panels are widgets themselves and can be stretched by specifying a "stretch" style property.

Arguments:

direction (String, optional):

The direction of the flow. One of

'horizontal' or 'vertical'. Defaults to 'vertical'.

wrap (Boolean, optional):

Whether to wrap children in the layout if there are too many to show in one line. Defaults to false.

Returns: ui.Panel.Layout

Map.getScale()

Returns the approximate pixel scale of the current map view, in meters.返回当前地图视图的大致像素比例尺(单位:米)。

No arguments.

Returns: Number|String

ui.Panel(widgetslayoutstyle)

A widget that can hold other widgets. Use panels to construct complex combinations of nested widgets.

Panels can be added to ui.root but not printed to the console with print().

可容纳其他部件的部件。使用面板可以构建嵌套 widget 的复杂组合。

面板可以添加到 ui.root 中,但不能使用 print() 打印到控制台。

Arguments:

widgets (List<ui.Widget>|ui.Widget, optional):

The list of widgets or a single widget to add to the panel. Defaults to an empty array.

layout (String|ui.Panel.Layout, optional):

The layout to use for this panel. If a string is passed in, it's taken as a shortcut to the layout constructor with that name. Defaults to 'flow'.

style (Object, optional):

An object of allowed CSS styles with their values to be set for this widget. See style() documentation.

Returns: ui.Panel

split(regex, flags)

Splits a string on a regular expression, Returning a list of strings.

Arguments:

this:string (String):

The string to split.

regex (String):

A regular expression to split on. If regex is the empty string, then the input string is split into individual characters.

flags (String, default: ""):

A string specifying the regular expression flag: 'i' (ignore case)

Returns: List

advance(delta, unit, timeZone)

Create a new Date by adding the specified units to the given Date.

Arguments:

this:date (Date)

delta (Float)

unit (String):

One of 'year', 'month' 'week', 'day', 'hour', 'minute', or 'second'.

timeZone (String, default: null):

The time zone (e.g. 'America/Los_Angeles'); defaults to UTC.

Returns: Date

ui.DateSlider(startendvalueperiodonChangedisabledstyle)

A draggable target that ranges linearly between two dates. The date slider can be configured to display dates of various interval sizes, including day, 8-day, and year. The value of the slider is displayed as a label alongside it.

在两个日期之间线性移动的可拖动目标。日期滑块可配置为显示不同间隔大小的日期,包括日、8 日和年。滑块的值会以标签的形式显示在旁边。

Arguments:

start (Date|Number|String, optional):

The start date, as a UTC timestamp, date string, or ee.Date. Defaults to one week ago.

end (Date|Number|String, optional):

The end date, as a UTC timestamp, date string, or ee.Date. Defaults to today.

value (Date|Number|String, optional):

The initial value. The value is an array consisting of the start and end date for the selected date range, but for convenience, it can be set by specifying the start date alone. Defaults to yesterday.

period (Number, optional):

The interval size for values on the slider in days. Defaults to one.

onChange (Function, optional):

A callback to fire when the slider's state changes. The callback is passed an ee.DateRange representing the slider's current value and the slider widget.

disabled (Boolean, optional):

Whether the slider is disabled. Defaults to false.

style (Object, optional):

An object of allowed CSS styles with their values to be set for this widget. Defaults to an empty object.

Returns: ui.DateSlider

Map.layers()

Returns the list of layers associated with the default map.返回与默认地图相关的图层列表。

No arguments.

Returns: ui.data.ActiveList<ui.Map.AbstractLayer>

ee.reset()

Reset the library to its base state. Useful for re-initializing to a different server.

No arguments.

style(colorpointSizepointShapewidthfillColorstylePropertyneighborhoodlineType)

Draw a vector collection for visualization using a simple style language.

Arguments:

this:collection (FeatureCollection):

The collection to draw.

color (String, default: "black"):

A default color (CSS 3.0 color value e.g. 'FF0000' or 'red') to use for drawing the features. Supports opacity (e.g.: 'FF000088' for 50% transparent red).

pointSize (Integer, default: 3):

The default size in pixels of the point markers.

pointShape (String, default: "circle"):

The default shape of the marker to draw at each point location. One of: circlesquarediamondcrosspluspentagramhexagramtriangletriangle_uptriangle_downtriangle_lefttriangle_rightpentagonhexagonstar5star6. This argument also supports the following Matlab marker abbreviations: osdx+ph^v<>.

width (Float, default: 2):

The default line width for lines and outlines for polygons and point shapes.

fillColor (String, default: null):

The color for filling polygons and point shapes. Defaults to 'color' at 0.66 opacity.

styleProperty (String, default: null):

A per-feature property expected to contain a dictionary. Values in the dictionary override any default values for that feature.

neighborhood (Integer, default: 5):

If styleProperty is used and any feature has a pointSize or width larger than the defaults, tiling artifacts can occur. Specifies the maximum neighborhood (pointSize + width) needed for any feature.

lineType (String, default: "solid"):

The default line style for lines and outlines of polygons and point shapes. Defaults to 'solid'. One of: solid, dotted, dashed.

Returns: Image

代码

//加载研究去
var region = 
    /* color: #d63000 */
    /* shown: false */
    /* displayProperties: [
      {
        "type": "rectangle"
      }
    ] */
    ee.Geometry.Polygon(
        [[[115.07616668803962, 41.27242300777961],
          [115.07616668803962, 39.345751077160806],
          [117.66894012553962, 39.345751077160806],
          [117.66894012553962, 41.27242300777961]]], null, false),

//设定研究去指定的矢量数据,需要二级级文件
    prov = ee.FeatureCollection("users/bqt2000204051/beijing");


// 确定地图中心点
Map.centerObject(region, 5)

// 在地图中心设定APP条形标签
Map.add(ui.Label('Weekly Average AQ Monitor of Indonesia', {fontWeight: 'bold', fontSize: '28px'}));


// 可视化设定
var pal = require('users/gena/packages:palettes');
var Jet = pal.misc.jet[7]
var BrBG = pal.colorbrewer.BrBG[10].reverse()
var Ice = pal.cmocean.Ice[7]
var Inferno = pal.matplotlib.inferno[7]

// 气溶胶指数
var aiVis = { 
  min: -1, 
  max: 2, 
  palette: Jet
};
// 密度(一氧化碳、臭氧)
var densVis = { 
  min: 0, 
  max: 0.15, 
  palette: Inferno
};
// 密度(甲醛、氮、硫)
var densVis2 = { 
  min: 0, 
  max: 0.0005, 
  palette: Inferno
};
// 摩尔分数(甲烷)
var molfractVis = {
  min: 1750, 
  max: 1900, 
  palette: BrBG
};
// 云分数
var fractVis = {
  min: 0, 
  max: 1, 
  palette: Ice
};
// 边界颜色设定
var boundVis = {fillColor: "#00000000", color: '#FFFFFF', width: 2};

// 彩条
// 彩条数量
var nStepsR = 15;

// 颜色条小部件
function ColorBar(palette) {
  return ui.Thumbnail({ 
    image: ee.Image.pixelLonLat().select(0),
    params: {
      bbox: [0, 0, nStepsR, 0.5],
      dimensions: '100x5',
      format: 'png',
      min: 0,
      max: nStepsR,
      palette: palette,
    },
    style: {stretch: 'horizontal', margin: '0px 8px'},
  });
}

// 图例样式
// 返回一个带标签的图例,其中有一个颜色条和代表最小值、中间值和最大值的三个标签。

function makeLegend(title, vis) {
  var colorBar = ColorBar(vis.palette);
  
  var minLabel = vis.min.toString();
  var maxLabel = vis.max.toString();
  var midLabel = ((vis.min + vis.max) / 2).toFixed(2).toString(); // Compute the middle value and format it
  
  var labels = ui.Panel(
    [
      ui.Label(minLabel, {margin: '4px 8px'}),
      ui.Label(midLabel, {margin: '4px 8px', textAlign: 'center', stretch: 'horizontal'}),
      ui.Label(maxLabel, {margin: '4px 8px'})
    ],
    ui.Panel.Layout.flow('horizontal')
  );
  
  return ui.Panel(
    [
      ui.Label(title, {fontWeight: 'bold'}),
      colorBar,
      labels
    ]
  );
}

// 图例标题和脚注的样式
var LEGEND_TITLE_STYLE = {
  fontSize: '20px',
  fontWeight: 'bold',
  stretch: 'horizontal',
  textAlign: 'left',
  margin: '4px',
};

var LEGEND_FOOTNOTE_STYLE = {
  fontSize: '12px',
  stretch: 'horizontal',
  textAlign: 'left',
  margin: '4px',
};

// 管理标签(可选)

var text = require('users/gena/packages:text')

var scale = Map.getScale() + 0.5

var labels = prov.map(function(feat){
  feat = ee.Feature(feat)
  var name = ee.String(feat.get("provinsi"))
  var centroid = feat.geometry().centroid()
  var t = text.draw(name, centroid, scale, {
    fontSize: 10,
    textColor: 'white',
    outlineWidth: 0.5,
    outlineColor: 'black'
  })
  return t
})
var labels_final = ee.ImageCollection(labels)

// 组装图例面板
var legendPanel = ui.Panel(
  [
    ui.Label('Legend', LEGEND_TITLE_STYLE),
    makeLegend('Aerosol Index', aiVis),
    makeLegend('Compund Density - CO, O3 (mol/m^2)', densVis),
    makeLegend('Compund Density - CH2O, SO2, NO2  (mol/m^2)', densVis2),
    makeLegend('Compound Consentration - CH4 (mole fraction)', molfractVis),
    makeLegend('Cloud Coverage (fraction)', fractVis),
    ui.Label('Source: Sentinel-5P (ESA Copernicus)', LEGEND_FOOTNOTE_STYLE),
  ],
  ui.Panel.Layout.flow('vertical'),
  {width: '300px', position: 'bottom-left'}
);

// 添加面板到地图界面中
Map.add(legendPanel);

/*
 * 日期配置
 */
 
// 从当前日期中剥离时间
// 首次加载的数据是前一天的数据
var firstload = ee.Date(new Date().toISOString().split('T')[0]).advance(-1, 'day');
print(firstload);

// 可通过滑块访问的首个日期
var start_period = ee.Date('2019-01-01'); 

// 获取开始和结束日期
var startDate = start_period.getInfo();
var endDate = firstload.getInfo();

// 确定初始日期
var initialDate = firstload;

// 滑块功能
var dateSlider = ui.DateSlider({
  start: startDate.value, 
  end: endDate.value, 
  value: initialDate,
  period: 7, // 7-day granularity.
  style: {width: '300px', padding: '10px', position: 'bottom-right'},
  onChange: updateVisualization // Function to call when date changes.
});

// 创建一个面板,用于放置日期滑块和小时下拉菜单。
var dateTimePanel = ui.Panel({
  widgets: [dateSlider],
  layout: ui.Panel.Layout.flow('horizontal'), // Align widgets horizontally
  style: {position: 'bottom-right'}
});

Map.add(dateTimePanel);

/*
 * 主要功能和函数
 */
 
// 根据所选日期和时间更新可视化效果的函数。
function updateVisualization() {
  // 删除之前的图层
  Map.layers().reset();
  
  // 初始化日期
  var selectedDate = dateSlider.getValue();
  
  var selectedDateString = selectedDate.toString();
  var startDateString = selectedDateString.split(',')[0];
  var startDateTime = ee.Date(Number(startDateString));

  //  将所选日期转换为 ee.Date
  var date = startDateTime;
  
  // 结束日期(每周)
  var endDateTime = startDateTime.advance(1, 'week');
  
  // 管理边界
  var bound = prov.style(boundVis);

  // 根据该时间范围过滤您的采集并更新地图
  // 从哨兵-5P 一氧化碳中加载特定图像
  var CO = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_CO");
  var filteredCO = CO.filterDate(startDateTime, endDateTime);
  var specificCO = filteredCO.select('CO_column_number_density').mean().clip(region);

  // 从哨兵-5P 甲醛检测仪加载特定图像
  var HCHO = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_HCHO");
  var filteredHCHO = HCHO.filterDate(startDateTime, endDateTime);
  var specificHCHO = filteredHCHO.select('tropospheric_HCHO_column_number_density').mean().clip(region);
  
  // 从 "哨兵-5P 二氧化氮 "中加载特定图像
  var NO2 = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_NO2");
  var filteredNO2 = NO2.filterDate(startDateTime, endDateTime);
  var specificNO2 = filteredNO2.select('tropospheric_NO2_column_number_density').mean().clip(region);
  
  // 从哨兵-5P 臭氧中加载特定图像
  var O3 = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_O3");
  var filteredO3 = O3.filterDate(startDateTime, endDateTime);
  var specificO3 = filteredO3.select('O3_column_number_density').mean().clip(region);
  
  // 从哨兵-5P 二氧化硫卫星加载特定图像
  var SO2 = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_SO2");
  var filteredSO2 = SO2.filterDate(startDateTime, endDateTime);
  var specificSO2 = filteredSO2.select('SO2_column_number_density').mean().clip(region);
  
  // 从哨兵-5P 紫外线气溶胶指数中加载特定图像
  var AI = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_AER_AI");
  var filteredAI = AI.filterDate(startDateTime, endDateTime);
  var specificAI = filteredAI.select('absorbing_aerosol_index').mean().clip(region);
  
  // 从哨兵-5P 甲烷中加载特定图像
  var CH4 = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_CH4");
  var filteredCH4 = CH4.filterDate(startDateTime, endDateTime);
  var specificCH4 = filteredCH4.select('CH4_column_volume_mixing_ratio_dry_air').mean().clip(region);
  
  // 从哨兵-5P 云加载特定图像 
  var CL = ee.ImageCollection("COPERNICUS/S5P/NRTI/L3_CLOUD");
  var filteredCL = CL.filterDate(startDateTime, endDateTime);
  var specificCL = filteredCL.select('cloud_fraction').mean().clip(region);

  // 显示数据
  Map.addLayer(specificCL, fractVis, 'Cloud Coverage', false);
  Map.addLayer(specificCH4, molfractVis, 'Methane (CH4)', false);
  Map.addLayer(specificSO2, densVis2, 'Sulfur Dioxide (SO2)', false);
  Map.addLayer(specificNO2, densVis2, 'Nitrogen Dioxide (NO2)', false);
  Map.addLayer(specificHCHO, densVis2, 'Formaldehyde (CH2O)', false);
  Map.addLayer(specificO3, densVis, 'Ozone (O3)', false);
  Map.addLayer(specificCO, densVis, 'Carbon Monoxide (CO)', false)
  Map.addLayer(specificAI, aiVis, 'Aerosol Index');
  
 // 添加管理边界
  Map.addLayer(bound, {}, 'Province', true, 0.7);
  Map.addLayer(labels_final, {}, 'Label', false)
}

// 初始可视化
updateVisualization();



结果

 

 

GitHub 加速计划 / sentine / Sentinel
22.24 K
7.98 K
下载
alibaba/Sentinel: Sentinel 是阿里巴巴开源的一款面向分布式服务架构的流量控制、熔断降级组件,提供实时监控、限流、降级和系统保护功能,适用于微服务治理场景。
最近提交(Master分支:2 个月前 )
195150bc * fix issue 2485 which occur oom when using async servlet request. * optimize imports * 1. fix the same issue in the webmvc-v6x 2. improve based on review comments 2 个月前
b78b09d3 2 个月前
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐