GEE+python
GEE之python环境配置
首先需要vpn和注册GEE账号[参考]
-
安装python和pip(略);安装Google的 python API的客户端,命令行如下:pip install google-api-python-client
-
安装鉴权验证依赖库:pip install pycryptodomex
-
安装GEE的python库:pip install earthengine-api
-
第一次运行GEE需要先验证GEE账户,命令行运行如下:earthengine authenticate或者ee.Authenticate()
如果出现gcloud command not found的错误,该用earthengine authenticate --auth_mode=notebook进行验证(有效期1周) -
运行、初始化GEE,检查本地环境是否搭建完成:python -c “import ee; ee.Initialize()”
这一步因为开了VPN,要设置代理:
import ee,os
os.environ['HTTP_PROXY'] = 'http://127.0.0.1:41091'
os.environ['HTTPS_PROXY'] = 'http://127.0.0.1:41091'
ee.Initialize()
代理的地址在 “设置->网络和internet->代理” 查看地址和端口。
注:其中可能遇到 “[WinError 10060] 由于连接方在一段时间没有正确答复…” 问题。一方面检查注册账号是否授权,电脑用户名中文也有可能造成问题。另一方面,检查科学上网软件应该是全局模式。
- jupyter lab
我原先用的sublimetext,不方便直观的看到数据,更不方便调用地图窗口了,故转而使用jupyter lab。
在cmd输入:pip install jupyter,pip install jupyter lab
运行GEE时最好开全局代理
学习
数据格式
.getInfo()可以把gee的数据格式转为正常的格式,比如我想遍历遥感影像
在gee平台使用java语句或者在python api循环一个影像集或者一个list,需要获取尺寸size,col.size(),但是这样返回的默认是ee.Number数据格式,是没法用GEE文档中以外的其它的方法遍历的,大家一般推荐的是GEE里面的map(),iterate(),但是有时候这个方法仍然有局限性。所以使用col.size().getInfo()即可。
Task
将矢量导出到asset或者Google drive 的时候,需要使用task,在GEE的java里面使用task,需要在右侧边栏查看task执行情况。对于python的API执行task时,会在GEE中同时开启任务。
保存矢量shp
-
导入:本地shp到asset
直接在GEE里面操作手动添加就好了 -
导出:roi到drive,再下载到本地
在GEE中画出roi中会出现这个,在GEE执行下面的代码将roi导出到drive。
var roi = /* color: #98ff00 */ee.FeatureCollection([geometry]);
//导出的格式写为SHP
Export.table.toDrive({
collection:roi,
description: "341521",
fileNamePrefix: "341521",
fileFormat: "SHP",
folder:'earthengine'
});
- GEEroi到asset
Google drive通过谷歌账号可以直接获取15G免费存储空间,Assests是GEE资源存放位置,它里面的资源是可以直接在GEE工作空间中使用,每个用户空间限制是250G,显然选择导入到GEE容量更大。
Export.table.toAsset({
collection:roi,
description: "tth_brg",
assetId:'users/2393251747/tth_brg'
});
保存img影像
- 保存img到asset
- 保存img到本地
分为从asset到本地,或者pythonAPI直接到本地。
Map()
map()函数在GEE和在python里面的概念是一致的,即遍历一个序列里面的每一个元素,对这些元素进行操作,然后返回一个新的序列。
例下,完成对影像集里面每一幅图的裁剪,从而形成新的裁剪后的影像集:
def clip(image):
return image.clip(aoi)
aoi_pnts=[[[114.09269762083298, 30.356153799649764],
[114.09269762083298, 30.318818747475646],
[114.12977647825485, 30.318818747475646],
[114.12977647825485, 30.356153799649764]]]
aoi = ee.Geometry.Polygon(aoi_pnts)
roi=ee.FeatureCollection(aoi)
L8_col = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR').filterBounds(roi).filter(ee.Filter.calendarRange(2012,2013,'year')).filter(ee.Filter.calendarRange(3,11,'month'))
valid_Col = L8_col.map(clip)
属性查询
在python里面用x.get(’…’).getInfo()
'…'可以是的内容通过propertyNames查看
var list=ee.Dictionary(ee.Dictionary(ee.Algorithms.Describe(image)).get('properties'))
print(list)
print(list.get('REFLECTIVE_LINES'))
//pretty code
var bandList = ee.List(ee.Dictionary(ee.Algorithms.Describe(image)).get('bands'));
var b1 = ee.Dictionary(bandList.get(0));
print(b1)
var dimensions = b1.get("dimensions");
dimensions = ee.List(dimensions);
print("dimensions", dimensions);
var sizex = ee.Number(dimensions.get(0));
var sizey = ee.Number(dimensions.get(1));
print("sizex", sizex);
print("sizey", sizey);
var crs_transform = b1.get("crs_transform");
print("crs_transform", crs_transform);
- 新建属性
- 设置属性值
制作GIF
有了裁剪过后的影像集,只需要调用getVideoThumbURL()函数就可以得到动图,点开打印的链接就可以看到了。
def mk_gif(collection):
visParams = {'bands':['B4','B3','B2'],'min':0,'max':2000}
url=collection.getVideoThumbURL(visParams)
print(url)
return url
这个是我做的一个gif,这里面的标签是怎么添加的呢?原本生成的动图里面是没有标签的,我在用影像集生成gif的时候同时把影像集的index保存到本地txt里面,就用下面这个代码:
indexList = collection.reduceColumns(ee.Reducer.toList(), ["system:index"]).get("list")
save_date=indexList.getInfo()
File = open(r"C:\Users\23932\Desktop\hello.txt", "w")
for date in save_date:
File.write(str(date) + ";")
File.close()
有了标签index序列之后,我用了这位的代码(python给gif添加文本),他是用的PIL库做的。我在这里设置的帧率0.5(每2秒一幅图),文本颜色白色。
可以看到这个位置的江心洲和心滩,5~10月的心滩都基本处于淹没状况,而江心洲出露面积基本无变化。
给影像集排序
按时间排序
col.sort(‘system:time_start’,True)
设置true的话是升序,false就是降序。
按裁剪区域的云量排序
首先制作裁剪后的数据集
然后写个函数用于计算一幅图的云量并同时设置为‘cloudcover’的值
最后排序
img.set(‘start_time’,date);
img.set({ ‘start_time’:date,‘end_time’:date.advance(1,‘month’)});
Img.setMulti({ ‘start_time’:date,‘end_time’:.advance(1,‘month’)});
img.propertyNames()
geemap
https://geemap.org/common/?h=ee_to_numpy#geemap.common.ee_to_numpy
ee_to_numpy
这个函数可以实现影像转numpy,这样很方便将数据放到本地来用python处理。
ee_to_numpy(ee_object, bands=None, region=None, properties=None, default_value=None)
使用它的过程中遇到许多困难
- region,它不是要素,而是geometry,这个geometry这样制造:
首先在GEE中圈出你要的范围,然后在代码框中点击这个
就可以看见具体geometry的坐标了。 - 其次问题是它的region是你范围的最小外接矩形,所以这里还是推荐在GEE先处理成MNDWI再导出。或者直接在GEE完成选择影像,水体提取在python做。
- 输入的img包含的波段必须要有同样的空间分辨率
aoi = ee.Geometry.Polygon(
[[[92.37187894329809, 34.23795235235915],
[92.37187894329809, 34.22063708300215],
[92.41633923992894, 34.22063708300215],
[92.41633923992894, 34.23795235235915]]], None, False);
rgb_img = geemap.ee_to_numpy(img, region = aoi)
- 缺陷,这个函数有一个致命的缺陷,它每次导出的数组大小,即aoi最小外接矩形所包含的像元个数必须小于等于262144,下面我写了一个函数来把大的roi切分为若干满足条件的roi:
面积最大来切分
def cut_poly_by_maxarea(pnts,maxarea):
#pnts=[[x],[y]]
#1.首先划分aoi
poly1=Polygon(zip(pnts[0],pnts[1]))
area=poly1.area
x,y=list(pnts[0]),list(pnts[1])
if area>=maxarea:
#需要切分
ids=area//maxarea
cut=(max(x)-min(x))/ids
sub_aoi,cut_X=[],[]
cut_x1,cut_x2=min(x),min(x)+cut
for i in range(int(ids)):
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
if not poly1.intersects(poly2):
cut_X.append(max(x))
break
elif poly1.intersection(poly2).area>maxarea:
small_cut=cut
while poly1.intersection(poly2).area>maxarea:
small_cut/=2
cut_x2=cut_x1+small_cut
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
cut_X.append(cut_x2)
else:
cut_X.append(cut_x2)
cut_x1,cut_x2=cut_x2,cut_x2+cut#换到下一个切分区域
else:pass
#return aoi,[0,0]
return cut_X
按最小外接矩形面积最大来切分
def cut_by_max_rectarea(pnts,maxarea):
def get_pnts(poly):
#poly是shapely的multipoly或者polygon
if poly.type=='MultiPolygon':
xy=[]
for i in poly:
xy+=list(zip(*i.exterior.coords.xy))
else:
xy=list(zip(*poly.exterior.coords.xy))
return list(zip(*xy))
def get_geometries(poly):
#poly是shapely的multipoly或者polygon
xy=[]
if poly.type=='MultiPolygon':
for i in poly:
xy.append(list(zip(*i.exterior.coords.xy)))
else:
xy.append(list(zip(*poly.exterior.coords.xy)))
return xy
#pnts=[[x],[y]]
#1.首先划分aoi
poly1=Polygon(zip(pnts[0],pnts[1]))
[x,y]=get_pnts(poly1)
area=(max(x)-min(x))*(max(y)-min(y))
if area>=maxarea:
#需要切分
sub_aoi,rel_coor=[],[]
cut=(max(x)-min(x))/(area//maxarea)
cut_x1,cut_x2=min(x),min(x)+cut#初始切分区域
while cut_x1<max(x):
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
poly_insec=poly1.intersection(poly2)
#计算满足条件的最小外接矩形
[sub_x,sub_y]=get_pnts(poly_insec)
sub_area=(max(sub_x)-min(sub_x))*(max(sub_y)-min(sub_y))
if sub_area>maxarea:
small_cut=cut/sub_area*maxarea*0.9
while sub_area>maxarea:
cut_x2=cut_x1+small_cut
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
poly_insec=poly1.intersection(poly2)
[sub_x,sub_y]=get_pnts(poly_insec)
sub_area=(max(sub_x)-min(sub_x))*(max(sub_y)-min(sub_y))
small_cut=small_cut*0.9
else:
cut=small_cut/0.9#定下该次的cut
sub_aoi.append(get_geometries(poly_insec))
else:
large_cut=cut
while sub_area<=maxarea and cut_x2<max(x):
large_cut=large_cut*1.1
cut_x2=cut_x1+large_cut
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
poly_insec=poly1.intersection(poly2)
[sub_x,sub_y]=get_pnts(poly_insec)
sub_area=(max(sub_x)-min(sub_x))*(max(sub_y)-min(sub_y))
else:
large_cut=large_cut/1.1
cut_x2=cut_x1+large_cut
poly2=Polygon([[cut_x1,max(y)],[cut_x2,max(y)],[cut_x2,min(y)],[cut_x1,min(y)]])
poly_insec=poly1.intersection(poly2)
[sub_x,sub_y]=get_pnts(poly_insec)
cut=large_cut#定下该次的cut
sub_aoi.append(get_geometries(poly_insec))
cut_x1,cut_x2=cut_x2,cut_x2+cut#换到下一个切分区域
else:
#不需要切分
sub_aoi=[zip(pnts[0],pnts[1])]
#return aoi,[0,0]
return sub_aoi
sub_aois=cut_by_max_rectarea(aoi_proj,262144*30**2)
上图便是按最小外接矩形面积不大于262144*30**2(分辨率是30m)来切分的结果,之后只需要遍历sub_aois分别来使用ee_to_numpy。
roi的调用
1 可以直接在gee里面画roi,然后代码里面创建geometry,geometry=ee.Geometry.Polygon(pnts)
roi=ee.FeatureCollection(geometry)
2 在gee里面画的roi导入到asset里面,这样代码直接调用
roi=ee.FeatureCollection(”users/xxxxx/xxxxxx”)
geometry=roi.geometry()
根据坐标获取数组
根据遍历multipolygon坐标来提取多个波段数组,可以被用来制作数据集。
工作流
- ROI:在GEE里面画roi,导出到GEE的asset,同时取得aoi的坐标放到程序中。
- 选择影像:在pythonAPI中筛选计算每幅裁剪影像的云量,并按云量从小到大将影像排序。再依次遍历影像并显示,人工判断是否需要该幅影像。需要的影像img,以txt格式保存影像信息到本地。
- 保存影像:把裁剪的影像以npy格式保存到本地,其中aoi比较大的先用我的算法切分为若干个sub_aoi,依次转numpy数组后再组合为完整数组,导出本地为npy文件,命名就是上面的txt命名,同时保存对应的geotrans和proj。不同分辨率的波段影像放在不同的子文件夹下。
- 融合:pan_sharpen提高分辨率
- 计算MNDWI得到mask
- 在本地python直接处理mask.npy。
注:融合在GEE里面我只找到了HSV sharpen,之后可以在本地合成tiff后再用gdal_sharpe
应用
画水系
如果不在线画的话,会需要很多遥感影像来对比着话水系,所以最方便的是在GEE里面直接画roi,然后把roi导入Google Drive再下载到本地。