"""Helpful functions for managing GEE assetsgeeViz.assetManagerLib includes functions for copying, deleting, uploading, changing permissions, and more."""""" Copyright 2025 Leah Campbell, Ian Housman, Nicholas Storey Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Pieces of code were taken from: https://github.com/tracek/gee_asset_manager"""# --------------------------------------------------------------------------# ASSETMANAGERLIB.PY# --------------------------------------------------------------------------# %%importgeeViz.geeViewimportsys,ee,os,shutil,subprocess,datetime,calendar,json,globimporttime,logging,pdbtaskLimit=10# %%############################################################################################## Functions to update ACL#############################################################################################
[docs]defupdateACL(assetName,writers=[],all_users_can_read=True,readers=[]):""" Updates the Access Control List (ACL) for a given GEE asset. Args: assetName (str): The name of the GEE asset. writers (list, optional): List of users with write access. Defaults to an empty list. all_users_can_read (bool, optional): Whether all users can read the asset. Defaults to True. readers (list, optional): List of users with read access. Defaults to an empty list. Returns: None Example: >>> updateACL('users/youruser/yourasset', writers=['user1'], all_users_can_read=False, readers=['user2']) """print("Updating permissions for: ",assetName)try:ee.data.setAssetAcl(assetName,json.dumps({"writers":writers,"all_users_can_read":all_users_can_read,"readers":readers,}),)exceptExceptionasE:print("Could not update permissions for: ",assetName)print(E)
[docs]deflistAssets(folder:str)->list[str]:""" Lists assets within a given asset folder or image collection. Args: folder (str): The path to the asset folder or image collection. Returns: list[str]: A list of asset IDs within the specified folder. Example: >>> listAssets('users/youruser/yourfolder') """return[i["id"]foriinee.data.listAssets({"parent":folder})["assets"]]
[docs]defbatchUpdateAcl(folder,writers=[],all_users_can_read=True,readers=[]):""" Updates the ACL for all assets under a given folder in GEE. Args: folder (str): The path to the folder in GEE. writers (list, optional): List of users with write access. Defaults to an empty list. all_users_can_read (bool, optional): Whether all users can read the assets. Defaults to True. readers (list, optional): List of users with read access. Defaults to an empty list. Returns: None Example: >>> batchUpdateAcl('users/youruser/yourfolder', writers=['user1'], all_users_can_read=False) """assets=ee.data.listAssets({"parent":folder})["assets"]folders=[aforainassetsifa["type"]=="FOLDER"]forsubFolderinfolders:print("Updating acl for:",subFolder)try:updateACL(subFolder["id"],writers,all_users_can_read,readers)except:print("Could not update",subFolder)allImages=walkFolders(folder)forimageinallImages:print("Updating acl for:",image)try:updateACL(image,writers,all_users_can_read,readers)except:print("Could not update",image)
############################################################################################## Functions for copying, deleting, etc. assets#############################################################################################
[docs]defbatchCopy(fromFolder,toFolder,outType="imageCollection"):""" Copies all assets from one folder to another in GEE. Args: fromFolder (str): The source folder path. toFolder (str): The destination folder path. outType (str, optional): The type of assets to copy ('imageCollection' or 'tables'). Defaults to 'imageCollection'. Returns: None Example: >>> batchCopy('users/youruser/source', 'users/youruser/dest') """ifoutType=="imageCollection":create_image_collection(toFolder)elifoutType=="tables":verify_path(toFolder)iftoFolder[-1]=="/":toFolder=toFolder[:-1]ifoutType=="imageCollection":images=walkFolders(fromFolder)elifoutType=="tables":images=walkFoldersTables(fromFolder)forimageinimages:out=toFolder+"/"+base(image)ifnotee_asset_exists(out):print("Copying: ",image)try:ee.data.copyAsset(image,out)exceptExceptionasE:print("Could not copy: ",image)print(E)else:print(out," already exists")
[docs]defcopyByName(fromFolder,toFolder,nameIdentifier,outType="imageCollection"):""" Copies assets from one folder to another based on a name identifier. Args: fromFolder (str): The source folder path. toFolder (str): The destination folder path. nameIdentifier (str): A substring to identify assets to copy. outType (str, optional): The type of assets to copy ('imageCollection' or 'tables'). Defaults to 'imageCollection'. Returns: None Example: >>> copyByName('users/youruser/source', 'users/youruser/dest', '2020') """ifoutType=="imageCollection":create_image_collection(toFolder)elifoutType=="tables":verify_path(toFolder)iftoFolder[-1]=="/":toFolder=toFolder[:-1]ifoutType=="imageCollection":images=walkFolders(fromFolder)elifoutType=="tables":images=walkFoldersTables(fromFolder)forimageinimages:ifnameIdentifierinimage:out=toFolder+"/"+base(image)print(out)try:ee.data.copyAsset(image,out)except:print(out,"Error: May already exist")
[docs]defmoveImages(images,toFolder,delete_original=False):""" Moves images to a new folder, optionally deleting the originals. Args: images (list): List of image paths to move. toFolder (str): The destination folder path. delete_original (bool, optional): Whether to delete the original images. Defaults to False. Returns: None Example: >>> moveImages(['users/youruser/img1', 'users/youruser/img2'], 'users/youruser/dest', delete_original=True) """create_image_collection(toFolder)forimageinimages:print("Copying",base(image))out=toFolder+"/"+base(image)ifnotee_asset_exists(out):try:ee.data.copyAsset(image,out)ifdelete_original:ee.data.deleteAsset(image)exceptExceptionasE:print("Error copying:",image)print(E)
[docs]defbatchDelete(Collection,type="imageCollection"):""" Deletes all assets in a collection. Args: Collection (str): The path to the collection. type (str, optional): The type of assets to delete ('imageCollection' or 'tables'). Defaults to 'imageCollection'. Returns: None Example: >>> batchDelete('users/youruser/collection') """iftype=="imageCollection":images=walkFolders(Collection)eliftype=="tables":images=walkFoldersTables(Collection)forimageinimages:print("Deleting: "+Collection+"/"+base(image))try:ee.data.deleteAsset(image)exceptExceptionasE:print("Could not delete: ",image)print(E)
[docs]defdeleteByName(Collection,nameIdentifier,type="imageCollection"):""" Deletes assets in a collection based on a name identifier. Args: Collection (str): The path to the collection. nameIdentifier (str): A substring to identify assets to delete. type (str, optional): The type of assets to delete ('imageCollection' or 'tables'). Defaults to 'imageCollection'. Returns: None Example: >>> deleteByName('users/youruser/collection', '2020') """iftype=="imageCollection":images=walkFolders(Collection)eliftype=="tables":images=walkFoldersTables(Collection)forimageinimages:ifnameIdentifierinimage:print("Deleting: "+Collection+"/"+base(image))try:ee.data.deleteAsset(image)exceptExceptionasE:print("Could not delete: ",image)print(E)
############################################################################################## Asset Info Queries#############################################################################################suffixes=["B","KB","MB","GB","TB","PB"]
[docs]defhumansize(nbytes):""" Converts a file size in bytes to a human-readable format. Args: nbytes (int): The size in bytes. Returns: str: The human-readable file size. Example: >>> humansize(1048576) '1 MB' """i=0whilenbytes>=1024andi<len(suffixes)-1:nbytes/=1024.0i+=1f=("%.2f"%nbytes).rstrip("0").rstrip(".")return"%s%s"%(f,suffixes[i])
[docs]defassetsize(asset):""" Prints the size of a GEE asset. Args: asset (str): The path to the GEE asset. Returns: None Example: >>> assetsize('users/youruser/yourasset') """header=ee.data.getInfo(asset)["type"]ifheader=="IMAGE_COLLECTION":collc=ee.ImageCollection(asset)size=collc.aggregate_array("system:asset_size")print("")print(str(asset)+" ===> "+str(humansize(sum(size.getInfo()))))print("Total number of items in collection: "+str(collc.size().getInfo()))elifheader=="IMAGE":collc=ee.Image(asset)print("")print(str(asset)+" ===> "+str(humansize(collc.get("system:asset_size").getInfo())))elifheader=="TABLE":collc=ee.FeatureCollection(asset)print("")print(str(asset)+" ===> "+str(humansize(collc.get("system:asset_size").getInfo())))elifheader=="FOLDER":b=subprocess.check_output("earthengine du "+asset+" -s",shell=True)num=subprocess.check_output("earthengine ls "+asset,shell=True)size=humansize(float(b.strip().split(" ")[0]))print("")print(str(asset)+" ===> "+str(size))print("Total number of items in folder: "+str(len(num.split("\n"))-1))
############################################################################################## Functions to Upload to Google Cloud Storage and Google Earth Engine Repository#############################################################################################
[docs]defupload_to_gcs(image_dir,gs_bucket,image_extension=".tif",copy_or_sync="copy",overwrite=False):""" Uploads files to Google Cloud Storage. Args: image_dir (str): The directory containing the images to upload. gs_bucket (str): The GCS bucket to upload to. image_extension (str, optional): The file extension of the images. Defaults to '.tif'. copy_or_sync (str, optional): Whether to copy or sync files ('copy' or 'sync'). Defaults to 'copy'. overwrite (bool, optional): Whether to overwrite existing files. Defaults to False. Returns: None Example: >>> upload_to_gcs('/tmp/images/', 'gs://mybucket') """ifgs_bucket.find("gs://")==-1:gs_bucket=f"gs://{gs_bucket}"overwrite_str="-n "ifoverwrite:overwrite_str=""ifcopy_or_sync=="copy":call_str=f'gsutil.cmd -m cp {overwrite_str}"{image_dir}*{image_extension}" {gs_bucket}'else:call_str=f'gsutil.cmd -m rsync {overwrite_str}"{image_dir}" {gs_bucket}'print(call_str)call=subprocess.Popen(call_str)call.wait()
[docs]defuploadTifToGCS(tif,gcsBucket,overwrite=False):""" Uploads a single TIFF file to Google Cloud Storage. Args: tif (str): The path to the TIFF file. gcsBucket (str): The GCS bucket to upload to. overwrite (bool, optional): Whether to overwrite existing files. Defaults to False. Returns: None Example: >>> uploadTifToGCS('/tmp/image.tif', 'gs://mybucket') """ifgcsBucket[-1]=='/':gcsBucket=gcsBucket[:-1]ifgcsBucket.find("gs://")==-1:gcsBucket=f"gs://{gcsBucket}"overwrite_str="-n"ifoverwrite:overwrite_str=""uploadCommand=f'gcloud storage cp {tif}{gcsBucket}{overwrite_str}'call=subprocess.Popen(uploadCommand,shell=True)call.wait()
[docs]defupload_to_gee(image_dir,gs_bucket,asset_dir,image_extension=".tif",resample_method="MEAN",band_names=[],property_list=[],):""" Uploads images to Google Earth Engine. Args: image_dir (str): The directory containing the images to upload. gs_bucket (str): The GCS bucket to upload to. asset_dir (str): The GEE asset directory. image_extension (str, optional): The file extension of the images. Defaults to '.tif'. resample_method (str, optional): The resampling method. Defaults to 'MEAN'. band_names (list, optional): List of band names. Defaults to an empty list. property_list (list, optional): List of properties for the images. Defaults to an empty list. Returns: None Example: >>> upload_to_gee('/tmp/images/', 'gs://mybucket', 'users/youruser/yourcollection') """create_image_collection(asset_dir)tifs=glob.glob(os.path.join(image_dir,image_extension))asset_dir=check_end(asset_dir)gs_files=map(lambdai:gs_bucket+os.path.basename(i),tifs)band_names=map(lambdai:{"id":i},band_names)i=0forgs_fileings_files:limitTasks(taskLimit)asset_name=asset_dir+base(gs_file)ifnotee_asset_exists(asset_name):print("Importing asset:",asset_name)properties=property_list[i]sl={"primaryPath":gs_file,"additionalPaths":[]}request={"id":asset_name,"tilesets":[{"sources":[sl]}],"bands":band_names,"properties":properties,"pyramidingPolicy":resample_method,}print(request)taskid=ee.data.newTaskId(1)[0]print(taskid)message=ee.data.startIngestion(taskid,request)print("Task message:",message)else:print(asset_name,"already exists")i+=1task_count=countTasks(False)whiletask_count>=1:running,ready=countTasks(True)print(running,"tasks running at:",now())print(ready,"tasks ready at:",now())task_count=running+readytime.sleep(5)
[docs]defcheck_end(in_path,add="/"):""" Ensures a trailing character is in the path. Args: in_path (str): The input path. add (str, optional): The character to add. Defaults to '/'. Returns: str: The modified path. Example: >>> check_end('users/youruser/collection') 'users/youruser/collection/' """ifin_path[-len(add):]!=add:out=in_path+addelse:out=in_pathreturnout
[docs]defbase(in_path):""" Gets the filename without extension. Args: in_path (str): The input path. Returns: str: The filename without extension. Example: >>> base('/tmp/image.tif') 'image' """returnos.path.basename(os.path.splitext(in_path)[0])
[docs]defwalkFolders(folder,images=[]):""" Walks down folders and gets all images. Args: folder (str): The folder path. images (list, optional): List of images. Defaults to an empty list. Returns: list: List of image paths. Example: >>> walkFolders('users/youruser/collection') """assets=ee.data.getList({"id":folder})folders=[str(i["id"])foriinassetsifi["type"]=="Folder"]imagesT=[str(i["id"])foriinassetsifi["type"]=="Image"]print(imagesT)foriinimagesT:ifinotinimages:images.append(i)iteration=2whilelen(folders)>0:print("Starting iteration",iteration)forfolderinfolders:print(folder)assets=ee.data.getList({"id":folder})folders=[str(i["id"])foriinassetsifi["type"]=="Folder"]imagesT=[str(i["id"])foriinassetsifi["type"]=="Image"]foriinimagesT:ifinotinimages:images.append(i)iteration+=1returnimages
[docs]defwalkFoldersTables(folder,tables=[]):""" Walks down folders and gets all tables. Args: folder (str): The folder path. tables (list, optional): List of tables. Defaults to an empty list. Returns: list: List of table paths. Example: >>> walkFoldersTables('users/youruser/collection') """assets=ee.data.getList({"id":folder})folders=[str(i["id"])foriinassetsifi["type"]=="Folder"]tablesT=[str(i["id"])foriinassetsifi["type"]=="Table"]print(tablesT)foriintablesT:ifinotintables:tables.append(i)iteration=2whilelen(folders)>0:print("Starting iteration",iteration)forfolderinfolders:print(folder)assets=ee.data.getList({"id":folder})folders=[str(i["id"])foriinassetsifi["type"]=="Folder"]tablesT=[str(i["id"])foriinassetsifi["type"]=="Table"]foriintablesT:ifinotintables:tables.append(i)iteration+=1returntables
[docs]defcheck_dir(in_path):""" Ensures the directory exists. Args: in_path (str): The directory path. Returns: None Example: >>> check_dir('/tmp/mydir') """ifos.path.exists(in_path)==False:print("Making dir:",in_path)os.makedirs(in_path)
[docs]defyear_month_day_to_seconds(year_month_day):""" Converts a date to seconds since epoch. Args: year_month_day (list): List containing year, month, and day. Returns: int: Seconds since epoch. Example: >>> year_month_day_to_seconds([2020, 1, 1]) """ymd=year_month_dayreturncalendar.timegm(datetime.datetime(ymd[0],ymd[1],ymd[2]).timetuple())
[docs]deflimitTasks(taskLimit):""" Limits the number of tasks running. Args: taskLimit (int): The maximum number of tasks. Returns: None Example: >>> limitTasks(10) """taskCount=countTasks()whiletaskCount>taskLimit:running,ready=countTasks(True)print(running,"tasks running at:",now())print(ready,"tasks ready at:",now())taskCount=running+readytime.sleep(10)
[docs]defcountTasks(break_running_ready=False):""" Counts the number of tasks. Args: break_running_ready (bool, optional): Whether to break running and ready tasks. Defaults to False. Returns: int or tuple: Total tasks or tuple of running and ready tasks. Example: >>> countTasks() >>> countTasks(True) """tasks=ee.data.getTaskList()running_tasks=len(filter(lambdai:i["state"]=="RUNNING",tasks))ready_tasks=len(filter(lambdai:i["state"]=="READY",tasks))ifnotbreak_running_ready:returnrunning_tasks+ready_taskselse:returnrunning_tasks,ready_tasks
[docs]defee_asset_exists(path):""" Checks if a GEE asset exists. Args: path (str): The asset path. Returns: bool: True if the asset exists, False otherwise. Example: >>> ee_asset_exists('users/youruser/yourasset') """returnTrueifee.data.getInfo(path)elseFalse
[docs]defcreate_image_collection(full_path_to_collection,properties=None):""" Creates an image collection in GEE. Args: full_path_to_collection (str): The full path to the collection. properties (dict, optional): Properties for the collection. Defaults to None. Returns: None Example: >>> create_image_collection('users/youruser/newcollection') """ifee_asset_exists(full_path_to_collection):print("Collection "+full_path_to_collection+" already exists")else:try:ee.data.createAsset({"type":ee.data.ASSET_TYPE_IMAGE_COLL},full_path_to_collection,properties=properties)print("New collection "+full_path_to_collection+" created")exceptExceptionasE:print("Could not create: ",full_path_to_collection)print(E)
[docs]defcreate_asset(asset_path,asset_type=ee.data.ASSET_TYPE_FOLDER,recursive=True):""" Creates an asset in GEE. Args: asset_path (str): The asset path. asset_type (str, optional): The type of asset. Defaults to ee.data.ASSET_TYPE_FOLDER. recursive (bool, optional): Whether to create nested folders. Defaults to True. Returns: None Example: >>> create_asset('users/youruser/newfolder') """project_root=f'{asset_path.split("assets")[0]}assets'sub_directories=f'{asset_path.split("assets/")[1]}'.split("/")iflen(sub_directories)>1andrecursive:print("Found the following sub directories: ",sub_directories)print("Will attempt to create them if they do not exist")path_temp=project_rootforsub_directoryinsub_directories[:-1]:path_temp=f"{path_temp}/{sub_directory}"create_asset(path_temp,asset_type=ee.data.ASSET_TYPE_FOLDER,recursive=False)ifee_asset_exists(asset_path):print("Asset "+asset_path+" already exists")else:try:print(asset_path)ee.data.createAsset({"type":asset_type},asset_path)print("New asset "+asset_path+" created")exceptExceptionasE:print("Could not create: ",asset_path)print(E)
[docs]defverify_path(path):""" Verifies the validity of a path. Args: path (str): The path to verify. Returns: None Example: >>> verify_path('users/youruser/collection') """response=ee.data.getInfo(path)ifnotresponse:logging.error("%s is not a valid destination. Make sure full path is provided e.g. users/user/nameofcollection ""or projects/myproject/myfolder/newcollection and that you have write access there.",path,)sys.exit(1)
[docs]defis_leap_year(year):""" Determines if a year is a leap year. Args: year (int): The year. Returns: bool: True if the year is a leap year, False otherwise. Example: >>> is_leap_year(2020) True """year=int(year)ifyear%4==0:ifyear%100==0andyear%400!=0:returnFalseelse:returnTrueelse:returnFalse
[docs]defnow(Format="%b %d %Y %H:%M:%S %a"):""" Gets the current readable date/time. Args: Format (str, optional): The format of the date/time. Defaults to '%b %d %Y %H:%M:%S %a'. Returns: str: The current date/time. Example: >>> now() """today=datetime.datetime.today()s=today.strftime(Format)d=datetime.datetime.strptime(s,Format)returnd.strftime(Format)
[docs]defjulian_to_calendar(julian_date,year):""" Converts a Julian date to a calendar date. Args: julian_date (int): The Julian date. year (int): The year. Returns: list: List containing year, month, and day. Example: >>> julian_to_calendar(32, 2020) [2020, 2, 1] """julian_date,year=int(julian_date),int(year)is_leap=is_leap_year(year)ifis_leap:leap,length=True,[31,29,31,30,31,30,31,31,30,31,30,31]else:leap,length=False,[31,28,31,30,31,30,31,31,30,31,30,31]ranges=[]start=1formonthinlength:stop=start+monthranges.append(range(start,stop))start=start+monthmonth_no=1forRangeinranges:ifjulian_dateinRange:mn=month_noday_no=1fordayinRange:ifday==julian_date:dn=day_noday_no+=1month_no+=1iflen(str(mn))==1:lmn="0"+str(mn)else:lmn=str(mn)iflen(str(dn))==1:ldn="0"+str(dn)else:ldn=str(dn)return[year,mn,dn]
[docs]defgetDate(year,month,day):""" Gets a date in ISO format. Args: year (int): The year. month (int): The month. day (int): The day. Returns: str: The date in ISO format. Example: >>> getDate(2020, 1, 1) '2020-01-01T00:00:00Z' """returndatetime.datetime(year,month,day).isoformat()+"Z"
[docs]defsetDate(assetPath,year,month,day):""" Sets the date for an asset. Args: assetPath (str): The asset path. year (int): The year. month (int): The month. day (int): The day. Returns: None Example: >>> setDate('users/youruser/yourasset', 2020, 1, 1) """ee.data.updateAsset(assetPath,{"start_time":getDate(year,month,day)},["start_time"])
[docs]defingestImageFromGCS(gcsURIs,assetPath,overwrite=False,bandNames=None,properties=None,pyramidingPolicy=None,noDataValues=None,):""" Ingests an image from Google Cloud Storage to GEE. Args: gcsURIs (list): List of GCS URIs. assetPath (str): The asset path in GEE. overwrite (bool, optional): Whether to overwrite existing assets. Defaults to False. bandNames (list, optional): List of band names. Defaults to None. properties (dict, optional): Properties for the asset. Defaults to None. pyramidingPolicy (list, optional): Pyramiding policy for the bands. Defaults to None. noDataValues (list, optional): No data values for the bands. Defaults to None. Returns: None Example: >>> ingestImageFromGCS(['gs://mybucket/image.tif'], 'users/youruser/yourasset') """ifoverwriteornotee_asset_exists(assetPath):taskID=ee.data.newTaskId(1)[0]create_image_collection(os.path.dirname(assetPath))ifstr(type(gcsURIs)).find("'str'")>-1:gcsURIs=[gcsURIs]ifbandNames!=NoneandpyramidingPolicy!=Noneandstr(type(pyramidingPolicy)).find("'str'")>-1:pyramidingPolicy=[pyramidingPolicy]*len(bandNames)ifbandNames!=NoneandnoDataValues!=Noneandstr(type(noDataValues)).find("'list'")==-1:noDataValues=[noDataValues]*len(bandNames)params={"name":assetPath,"tilesets":[{"sources":[{"uris":gcsURIs}]}]}ifbandNames!=None:bnDict=[]fori,bninenumerate(bandNames):bnDictT={"id":bn,"tileset_band_index":i}ifpyramidingPolicy!=None:bnDictT["pyramiding_policy"]=pyramidingPolicy[i]ifnoDataValues!=None:bnDictT["missing_data"]={"values":[noDataValues[i]]}bnDict.append(bnDictT)params["bands"]=bnDictdeffixDate(propIn,propOut):ifpropIninproperties.keys():d=properties[propIn]ifstr(type(d)).find("ee.ee_date.Date")>-1:d=d.format("YYYY-MM-dd").cat("T").cat(d.format("HH:mm:SS")).cat("Z").getInfo()params[propOut]=dproperties.pop(propIn)ifproperties!=None:fixDate("system:time_start","start_time")fixDate("system:time_end","end_time")params["properties"]=propertiesprint("Ingestion manifest:",params)ee.data.startIngestion(taskID,params,overwrite)print("Starting ingestion task:",assetPath)else:print(assetPath,"already exists")
[docs]defingestFromGCSImagesAsBands(gcsURIs,assetPath,overwrite=False,properties=None,):""" Ingests multiple images from Google Cloud Storage as bands of a single GEE image. Args: gcsURIs (list): List of GCS URIs or dictionaries with band information. assetPath (str): The asset path in GEE. overwrite (bool, optional): Whether to overwrite existing assets. Defaults to False. properties (dict, optional): Properties for the asset. Defaults to None. Returns: None Example: >>> ingestFromGCSImagesAsBands([{'gcsURI': 'gs://mybucket/band1.tif', 'bandName': 'B1'}], 'users/youruser/yourasset') """ifoverwriteornotee_asset_exists(assetPath):taskID=ee.data.newTaskId(1)[0]create_image_collection(os.path.dirname(assetPath))ifstr(type(gcsURIs)).find("'str'")>-1:gcsURIs=[{"gcsURI":gcsURIs}]elifisinstance(gcsURIs,list):fori,iteminenumerate(gcsURIs):ifnotisinstance(item,dict):gcsURIs[i]={'gcsURI':item}params={"name":assetPath,"tilesets":[],"bands":[]}fori,iteminenumerate(gcsURIs):if"gcsURI"notinitem:raiseValueError(f"ERROR: The 'gcsURIs' parameter must be a string, list, or list of dictionaries with a key for 'gcsURI'")tileset_entry={"id":f"tileset_for_band{i+1}","sources":[{"uris":[item["gcsURI"]]}]}band_entry={"tileset_id":f"tileset_for_band{i+1}"}if"bandName"initem:band_entry["id"]=str(item["bandName"])else:band_entry["id"]=f"Band{i+1}"if"pyramidingPolicy"initem:band_entry["pyramidingPolicy"]=item["pyramidingPolicy"]if"noDataValue"initem:band_entry["missing_data"]={"values":[item["noDataValue"]]}params["tilesets"].append(tileset_entry)params["bands"].append(band_entry)deffixDate(propIn,propOut):ifpropIninproperties.keys():d=properties[propIn]ifstr(type(d)).find("ee.ee_date.Date")>-1:d=d.format("YYYY-MM-dd").cat("T").cat(d.format("HH:mm:SS")).cat("Z").getInfo()params[propOut]=dproperties.pop(propIn)ifproperties!=None:fixDate("system:time_start","start_time")fixDate("system:time_end","end_time")params["properties"]=propertiesprint("Ingestion manifest:",params)ee.data.startIngestion(taskID,params,overwrite)print("Starting ingestion task:",assetPath)else:print(assetPath,"already exists")
[docs]defuploadToGEEImageAsset(localTif,gcsBucket,assetPath,overwrite=False,bandNames=None,properties=None,pyramidingPolicy=None,noDataValues=None,parallel_threshold="150M",gsutil_path="C:/Program Files (x86)/Google/Cloud SDK/google-cloud-sdk/bin/gsutil.cmd",):""" Uploads an image to GEE as an asset. Args: localTif (str): Path to the local TIFF file. gcsBucket (str): The GCS bucket to upload to. assetPath (str): The asset path in GEE. overwrite (bool, optional): Whether to overwrite existing assets. Defaults to False. bandNames (list, optional): List of band names. Defaults to None. properties (dict, optional): Properties for the asset. Defaults to None. pyramidingPolicy (list, optional): Pyramiding policy for the bands. Defaults to None. noDataValues (list, optional): No data values for the bands. Defaults to None. parallel_threshold (str, optional): Threshold for parallel uploads. Defaults to '150M'. gsutil_path (str, optional): Path to the gsutil command. Defaults to a default path. Returns: None Example: >>> uploadToGEEImageAsset('/tmp/image.tif', 'gs://mybucket', 'users/youruser/yourasset') """localTifs=glob.glob(localTif)gcsURIs=[gcsBucket+"/"+os.path.basename(tif)fortifinlocalTifs]uploadCommand='"{}" -o "GSUtil:parallel_composite_upload_threshold="{}" " -m cp -n -r {}{}'.format(gsutil_path,parallel_threshold,localTif,gcsBucket)call=subprocess.Popen(uploadCommand)call.wait()ingestImageFromGCS(gcsURIs,assetPath,overwrite=overwrite,bandNames=bandNames,properties=properties,pyramidingPolicy=pyramidingPolicy,noDataValues=noDataValues,)
[docs]defuploadToGEEAssetImagesAsBands(tif_dict,gcsBucket,assetPath,overwrite=False,properties=None,):""" Uploads images to GCS and manifests them as bands of a single GEE image. Args: tif_dict (dict): Dictionary of TIFF files and their properties. gcsBucket (str): The GCS bucket to upload to. assetPath (str): The asset path in GEE. overwrite (bool, optional): Whether to overwrite existing assets. Defaults to False. properties (dict, optional): Properties for the asset. Defaults to None. Returns: None Example: >>> tif_dict = {'/tmp/band1.tif': {'bandName': 'B1'}, '/tmp/band2.tif': {'bandName': 'B2'}} >>> uploadToGEEAssetImagesAsBands(tif_dict, 'gs://mybucket', 'users/youruser/yourasset') """ifnotisinstance(tif_dict,dict):raiseValueError("ERROR: tif_dict must be a dictionary.")fortifintif_dict.keys():print(f"Uploading {tif} to GCS...")uploadTifToGCS(tif,gcsBucket)tif_dict[tif]["gcsURI"]=gcsBucket+"/"+os.path.basename(tif)print(f"Ingesting GCS images as bands...")ingestFromGCSImagesAsBands(tif_dict.values(),assetPath,overwrite=overwrite,properties=properties,)