关于生成Sentinel-2 L1C逐像素的太阳天顶角、太阳方位角、传感器天顶角、传感器方位角tif

Sentinel-2 L1C的太阳角度是以XML文件给的,在I:\S2B_MSIL1C_20201106T134209_N0500_R124_T22KCA_20230410T094912\S2B_MSIL1C_20201106T134209_N0500_R124_T22KCA_20230410T094912.SAFE\GRANULE\L1C_T22KCA_A019169_20201106T134212\MTD_TL.xml这个路径下面,但是是给的5km一个角度值,但是实际项目需要逐像素的角度文件,所以就需要对这个进行处理
主要利用python解析xml,用arcpy进行插值计算

import xml.etree.ElementTree as ET
import numpy as np
import math
from osgeo import ogr,osr
from osgeo import gdal
import os
import arcpy

from math import sqrt, cos, sin, tan, pi, asin, acos, atan, atan2
class OutOfRangeError(Exception):
	pass
a = 6378137.0                   # WGS 84 semi-major axis in meters
b = 6356752.314                 # WGS 84 semi-minor axis in meters
ecc = 1.0 - b / a * b / a       # WGS 84 ellipsoid eccentricity
todeg = 180.0 / pi		# Converts radians to degrees
K0 = 0.9996

E = 0.00669438
E2 = E * E
E3 = E2 * E
E_P2 = E / (1.0 - E)

SQRT_E = math.sqrt(1 - E)
_E = (1 - SQRT_E) / (1 + SQRT_E)
_E2 = _E * _E
_E3 = _E2 * _E
_E4 = _E3 * _E
_E5 = _E4 * _E

M1 = (1 - E / 4 - 3 * E2 / 64 - 5 * E3 / 256)
M2 = (3 * E / 8 + 3 * E2 / 32 + 45 * E3 / 1024)
M3 = (15 * E2 / 256 + 45 * E3 / 1024)
M4 = (35 * E3 / 3072)

P2 = (3. / 2 * _E - 27. / 32 * _E3 + 269. / 512 * _E5)
P3 = (21. / 16 * _E2 - 55. / 32 * _E4)
P4 = (151. / 96 * _E3 - 417. / 128 * _E5)
P5 = (1097. / 512 * _E4)

R = 6378137

ZONE_LETTERS = "CDEFGHJKLMNPQRSTUVWXX"
def 创建矢量点(AngleObs,shpname,raster_file):
    raster_ds = gdal.Open(raster_file)
    raster_srs = osr.SpatialReference()
    raster_srs.ImportFromWkt(raster_ds.GetProjection())
    driver = ogr.GetDriverByName("ESRI Shapefile")
    #output_layer = output_ds.CreateLayer("new_layer_name", srs=raster_srs, geom_type=ogr.wkbPolygon)
    data_source = driver.CreateDataSource(shpname)
    layer = data_source.CreateLayer('new_points', srs=raster_srs, geom_type=ogr.wkbPoint)
    f_ = ogr.FieldDefn('sunZenith', ogr.OFTReal)
    f_.SetWidth(32)
    f_.SetPrecision(16)
    layer.CreateField(f_)
    f_ = ogr.FieldDefn('sunAzimuth', ogr.OFTReal)
    f_.SetWidth(32)
    f_.SetPrecision(16)
    layer.CreateField(f_)
    for angle in AngleObs['obs']:
        point = ogr.Geometry(ogr.wkbPoint)
        point.AddPoint(angle[0], angle[1])
        feature = ogr.Feature(layer.GetLayerDefn())
        feature.SetField('sunZenith', angle[2])
        feature.SetField('sunAzimuth', angle[3])
        feature.SetGeometry(point)
        layer.CreateFeature(feature)

def from_latlon(latitude, longitude, force_zone_number=None):
    if not -80.0 <= latitude <= 84.0:
        raise OutOfRangeError('latitude out of range (must be between 80 deg S and 84 deg N)')
    if not -180.0 <= longitude <= 180.0:
        raise OutOfRangeError('longitude out of range (must be between 180 deg W and 180 deg E)')
    lat_rad = math.radians(latitude)
    lat_sin = math.sin(lat_rad)
    lat_cos = math.cos(lat_rad)

    lat_tan = lat_sin / lat_cos
    lat_tan2 = lat_tan * lat_tan
    lat_tan4 = lat_tan2 * lat_tan2

    if force_zone_number is None:
        zone_number = latlon_to_zone_number(latitude, longitude)
    else:
        zone_number = force_zone_number

    zone_letter = latitude_to_zone_letter(latitude)

    lon_rad = math.radians(longitude)
    central_lon = zone_number_to_central_longitude(zone_number)
    central_lon_rad = math.radians(central_lon)

    n = R / math.sqrt(1 - E * lat_sin**2)
    c = E_P2 * lat_cos**2

    a = lat_cos * (lon_rad - central_lon_rad)
    a2 = a * a
    a3 = a2 * a
    a4 = a3 * a
    a5 = a4 * a
    a6 = a5 * a

    m = R * (M1 * lat_rad -
             M2 * math.sin(2 * lat_rad) +
             M3 * math.sin(4 * lat_rad) -
             M4 * math.sin(6 * lat_rad))

    easting = K0 * n * (a +
                        a3 / 6 * (1 - lat_tan2 + c) +
                        a5 / 120 * (5 - 18 * lat_tan2 + lat_tan4 + 72 * c - 58 * E_P2)) + 500000

    northing = K0 * (m + n * lat_tan * (a2 / 2 +
                                        a4 / 24 * (5 - lat_tan2 + 9 * c + 4 * c**2) +
                                        a6 / 720 * (61 - 58 * lat_tan2 + lat_tan4 + 600 * c - 330 * E_P2)))

    if latitude < 0:
        northing += 10000000

    return easting, northing, zone_number, zone_letter


def latitude_to_zone_letter(latitude):
    if -80 <= latitude <= 84:
        return ZONE_LETTERS[int(latitude + 80) >> 3]
    else:
        return None


def latlon_to_zone_number(latitude, longitude):
    if 56 <= latitude < 64 and 3 <= longitude < 12:
        return 32

    if 72 <= latitude <= 84 and longitude >= 0:
        if longitude <= 9:
            return 31
        elif longitude <= 21:
            return 33
        elif longitude <= 33:
            return 35
        elif longitude <= 42:
            return 37

    return int((longitude + 180) / 6) + 1


def zone_number_to_central_longitude(zone_number):
    return (zone_number - 1) * 6 - 180 + 3
# Inverse (X/Y to lat/long) UTM projection
def utm_inv( Zone, X, Y, a=6378137.0, b=6356752.31414 ):
        if Zone < 0 :
                FNorth = 10000000.0     # Southern hemisphere False Northing
        else:
                FNorth = 0.0	        # Northern hemisphere False Northing
        FEast = 500000.0                # UTM False Easting
        Scale = 0.9996                  # Scale at CM (UTM parameter)
        LatOrigin = 0.0                 # Latitude origin (UTM parameter)
        CMDeg = -177 + (abs(int(Zone))-1)*6
        CM = float(CMDeg)*pi/180.0      # Central meridian (based on zone)
        ecc = 1.0 - b/a*b/a
        ep = ecc/(1.0-ecc)
        M0 = a*((1.0-ecc*(0.25+ecc*(3.0/64.0+ecc*5.0/256.0)))*LatOrigin
               -ecc*(0.375+ecc*(3.0/32.0+ecc*45.0/1024.0))*sin(2.0*LatOrigin)
               +ecc*ecc*(15.0/256.0+ecc*45.0/1024.0)*sin(4.0*LatOrigin)
               -ecc*ecc*ecc*35.0/3072.0*sin(6.0*LatOrigin))
        M = M0+(Y-FNorth)/Scale
        Mu = M/(a*(1.0-ecc*(0.25+ecc*(3.0/64.0+ecc*5.0/256.0))))
        e1 = (1.0-sqrt(1-ecc))/(1.0+sqrt(1.0-ecc))
        Phi1 = Mu+(e1*(1.5-27.0/32.0*e1*e1)*sin(2.0*Mu)
                   +e1*e1*(21.0/16.0-55.0/32.0*e1*e1)*sin(4.0*Mu)
                   +151.0/96.0*e1*e1*e1*sin(6.0*Mu)
                   +1097.0/512.0*e1*e1*e1*e1*sin(8.0*Mu))  
        slat = sin(Phi1)
        clat = cos(Phi1)
        Rn1 = a/sqrt(1.0-ecc*slat*slat)
        T1 = slat*slat/clat/clat
        C1 = ep*clat*clat
        R1 = Rn1*(1.0-ecc)/(1.0-ecc*slat*slat)
        D = (X-FEast)/Rn1/Scale
        # Calculate Lat/Lon
        Lat = Phi1 - (Rn1*slat/clat/R1*(D*D/2.0
                        -(5.0+3.0*T1+10.0*C1-4.0*C1*C1-9.0*ep)*D*D*D*D/24.0
                        +(61.0+90.0*T1+298.0*C1+45.0*T1*T1-252.0*ep-3.0*C1*C1)*D*D*D*D*D*D/720.0))
        Lon = CM + (D-(1.0+2.0*T1+C1)*D*D*D/6.0+(5.0-2.0*C1+28.0*T1-3.0*C1*C1+8.0*ep+24.0*T1*T1)
                    *D*D*D*D*D/120.0)/clat
        
        return (Lat, Lon)

def LOSVec( Lat, Lon, Zen, Az ):
	LSRx = ( -sin(Lon), cos(Lon), 0.0 )
	LSRy = ( -sin(Lat)*cos(Lon), -sin(Lat)*sin(Lon), cos(Lat) )
	LSRz = ( cos(Lat)*cos(Lon), cos(Lat)*sin(Lon), sin(Lat) )
	LOS = ( sin(Zen)*sin(Az), sin(Zen)*cos(Az), cos(Zen) )
	Sat = ( LOS[0]*LSRx[0] + LOS[1]*LSRy[0] + LOS[2]*LSRz[0],
	        LOS[0]*LSRx[1] + LOS[1]*LSRy[1] + LOS[2]*LSRz[1],
	        LOS[0]*LSRx[2] + LOS[1]*LSRy[2] + LOS[2]*LSRz[2] )
	Rn = a / sqrt( 1.0 - ecc *sin(Lat)*sin(Lat))
	Gx = ( Rn*cos(Lat)*cos(Lon),
		Rn*cos(Lat)*sin(Lon),
		Rn*(1-ecc)*sin(Lat) )
	return ( Sat, Gx )
def get_angleobssun( XML_File ):
    tree = ET.parse(XML_File)
    root = tree.getroot()
    for child in root:
            if child.tag[-12:] == 'General_Info':
                geninfo = child
            if child.tag[-14:] == 'Geometric_Info':
                geoinfo = child

    for segment in geninfo:
        if segment.tag == 'TILE_ID':
            tile_id = segment.text.strip()

    for segment in geoinfo:
        if segment.tag == 'Tile_Geocoding':
            frame = segment
        if segment.tag == 'Tile_Angles':
                angles = segment

    for box in frame:
        if box.tag == 'HORIZONTAL_CS_NAME':
            czone = box.text.strip()[-3:]
            hemis = czone[-1:]
            zone = int(czone[:-1])
        if box.tag == 'Size' and box.attrib['resolution'] == '60':
            for field in box:
                if field.tag == 'NROWS':
                    nrows = int(field.text)
                if field.tag == 'NCOLS':
                    ncols = int(field.text)
        if box.tag == 'Geoposition' and box.attrib['resolution'] == '60':
            for field in box:
                if field.tag == 'ULX':
                    ulx = float(field.text)
                if field.tag == 'ULY':
                    uly = float(field.text)
    if hemis == 'S':
        lzone = -zone
    else:
        lzone = zone
    Angle = { 'zone' : zone, 'hemis' : hemis, 'nrows' : nrows, 'ncols' : ncols, 'ul_x' : ulx, 'ul_y' : uly, 'obs' : [] }
    sunangle = { 'zone' : zone, 'hemis' : hemis, 'nrows' : nrows, 'ncols' : ncols, 'ul_x' : ulx, 'ul_y' : uly, 'obs' : [] }
    for angle in angles:
        #print(angle.tag)
        if angle.tag == 'Sun_Angles_Grid':	
            for bset in angle:
                if bset.tag == 'Zenith':
                    zenith = bset
                if bset.tag == 'Azimuth':
                    azimuth = bset	
            for field in zenith:
                if field.tag == 'COL_STEP':
                    col_step = int(field.text)
                if field.tag == 'ROW_STEP':
                    row_step = int(field.text)
                if field.tag == 'Values_List':
                    zvallist = field
            for field in azimuth:
                if field.tag == 'Values_List':
                    avallist = field
            for rindex in range(len(zvallist)):
                zvalrow = zvallist[rindex]
                avalrow = avallist[rindex]
                zvalues = zvalrow.text.split(' ')
                avalues = avalrow.text.split(' ')
                values = list(zip( zvalues, avalues ))
                ycoord = uly - rindex * row_step
                #for cindex in values:
                for cindex in range(len(values)):
                    xcoord = ulx + cindex * col_step
                    (lat, lon) = utm_inv( lzone, xcoord, ycoord )
                    if ( values[cindex][0] != 'NaN' and values[cindex][1] != 'NaN' ):
                        zen = float( values[cindex][0] ) / todeg    #转换为弧度制
                        az = float( values[cindex][1] ) / todeg
                        zen1 = float( values[cindex][0] ) 
                        az1 = float( values[cindex][1] ) 
                        ( Sat, Gx ) = LOSVec( lat, lon, zen, az )
                        observe = [xcoord, ycoord, Sat, Gx ]
                        Angle['obs'].append( observe )
                        sunobserve = [xcoord, ycoord, zen1, az1 ]
                        sunangle['obs'].append( sunobserve )
    return (tile_id, Angle, sunangle)

XML_File='I:\\HLSwGF6\\bx11_6\S2B_MSIL2A_20201106T134209_N0214_R124_T22KCA_20201106T160551\\S2B_MSIL2A_20201106T134209_N0214_R124_T22KCA_20201106T160551.SAFE\\GRANULE\\L2A_T22KCA_A019169_20201106T134212\\MTD_TL.xml'

# Sudipta spatial subset setting
sul_lat = sul_lon = slr_lat = slr_lon = None
(Tile_ID, AngleObs,sunangles) = get_angleobssun( XML_File )
Tile_Base = Tile_ID.split('.')
# 应该是使gdal库支持中文编码、中文字段、中文路径
gdal.SetConfigOption("GDAL_ARRAY_OPEN_BY_FILENAME","TRUE")
创建矢量点(sunangles,'D:\\文献\\1-s2.0-S0034425717303991-mmc1\\point','I:\\HLSwGF6\\bx11_6\\S2B_MSIL1C_20201106T134209_N0500_R124_T22KCA_20230410T094912\\S2B_MSIL1C_20201106T134209_N0500_R124_T22KCA_20230410T094912.SAFE\\GRANULE\\L1C_T22KCA_A019169_20201106T134212\\IMG_DATA\\T22KCA_20201106T134209_B03.jp2')
#插值为30m的tif
arcpy.gp.Idw_sa("D:\\文献\\1-s2.0-S0034425717303991-mmc1\\point\\new_points.shp", "sunAzimuth", "D:\\文献\\1-s2.0-S0034425717303991-mmc1\\point\\output4.tif", 30, 2)

传感器的两个角度是从文献中下载的代码,贴出文章名可以去官网下载,亲测可以直接使用
Sentinel-2 MultiSpectral Instrument (MSI) data processing for aquatic science applications: Demonstrations and validations
Supplementary data to this article can be found online at http://dx.doi.org/10.1016/j.rse.2017.08.033.

  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
Sentinel-2是欧空局(ESA)为欧洲卫星应用规划组织(Copernicus)计划开发的地球观测卫星,它搭载了多光谱传感器,用于监测地球表面的变化和环境状况。 Sentinel-2的传感器主要包括多光谱成像仪(MSI)和太阳光监测仪(SUN-MON)。多光谱成像仪由13个波段组成,从可见到近红外范围,波长分别为443 nm、490 nm、560 nm、665 nm、705 nm、740 nm、783 nm、842 nm、865 nm、945 nm、1375 nm、1610 nm和2190 nm。这些波段可以用于收集地表的不同特征信息,如植被、土壤、水域等。多光谱成像仪还具有高分辨率(10米至60米),可以捕捉到细节丰富的影像。 太阳光监测仪主要用来监测太阳辐射的强度和变化,以便对多光谱成像仪的观测数据进行校正和辐射校准。它能够提供太阳辐射的波长范围和太阳顶角的观测数据,从而提高数据的准确性和可靠性。 Sentinel-2的传感器数据将用于支持各种应用领域,如土地覆盖和土地利用监测、农业管理、水资源管理、森林监测、环境质量评估等。它可以提供全球范围的高空间分辨率的影像,帮助科学家、政府和企业更好地了解地球的自然和人为变化。 总之,Sentinel-2搭载了多光谱传感器,可以获取多个波段的地表影像和太阳辐射数据,用于监测地球表面的不同特征和环境状况。这些数据可以应用于各个领域的研究和决策支持。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值