Giter VIP home page Giter VIP logo

eemont's Introduction

Google Scholar Badge ResearchGate Badge ORCID Badge LinkedIn Badge Mastodon Badge Twitter Badge

Hey! I'm David but you can call me Dave! ๐Ÿ‘‹

Colombian guy ๐Ÿ‡จ๐Ÿ‡ด living in Germany ๐Ÿ‡ฉ๐Ÿ‡ช

I'm the creator of eemont, Awesome Spectral Indices, spectral, spyndex and co-creator of eeExtra and easystac (with the incredible @csaybar). Check more in this star-history chart!

Star History Chart

I'm a Research Assistant at the Remote Sensing Centre for Earth System Research (RSC4Earth) and a PhD Student at the University of Leipzig (Leipzig, Germany). I have a MSc in Data Science from the University of Cantabria (Santander, Spain) and a BEng in Topographic Engineering from the University of Valle (Cali, Colombia).

NOTE: If you want, you can ask me about Remote Sensing, Data Science, Google Earth Engine and Memes :)

Ok! That was it, stay safe and avoid dying today!

eemont's People

Contributors

aazuspan avatar actions-user avatar davemlz avatar giswqs avatar kbarnhart avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

eemont's Issues

Handling invalid methods for ee.Image.maskClouds with Sentinel-2

Hi @davemlz, I came across a minor issue when cloud masking Sentinel-2 imagery: If you pass an invalid method to ee.Image.maskClouds or ee.ImageCollection.maskClouds (like accidentally using method=prob instead of method=cloud_prob) it throws an unhelpful error that's tough to debug. If it could raise an exception that warns you about the bad method and/or gives you a list of valid methods that would be awesome :)

There's also a line in the docstring that mentions using the method prob instead of cloud_prob that's a little confusing.

Recreating

Run:

# Use any invalid "method" with Sentinel-2
ee.Image("COPERNICUS/S2_SR/20170328T083601_20170328T084228_T35RNK").maskClouds(method="prob")

Raises:

---------------------------------------------------------------------------
UnboundLocalError                         Traceback (most recent call last)
/tmp/ipykernel_10788/3049107977.py in <module>
----> 1 ee.Image("COPERNICUS/S2_SR/20170328T083601_20170328T084228_T35RNK").maskClouds(method="prob")

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/image.py in maskClouds(self, method, prob, maskCirrus, maskShadows, scaledImage, dark, cloudDist, buffer, cdi)
    965     >>> S2 = ee.ImageCollection('COPERNICUS/S2_SR').first().maskClouds(prob = 75,buffer = 300,cdi = -0.5)
    966     """
--> 967     return _maskClouds(
    968         self,
    969         method,

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/common.py in _maskClouds(self, method, prob, maskCirrus, maskShadows, scaledImage, dark, cloudDist, buffer, cdi)
    967     else:
    968         if isinstance(self, ee.image.Image):
--> 969             masked = lookup[platformDict["platform"]](self)
    970         elif isinstance(self, ee.imagecollection.ImageCollection):
    971             if platformDict["platform"] == "COPERNICUS/S2_SR":

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/common.py in S2(args)
    797                 S2Masked = CDI(S2Masked)
    798             if maskShadows:
--> 799                 S2Masked = get_shadows(S2Masked)
    800             S2Masked = apply_mask(clean_dilate(S2Masked))
    801         elif isinstance(self, ee.imagecollection.ImageCollection):

UnboundLocalError: local variable 'S2Masked' referenced before assignment

toPandas

Hello @davemlz! Thank you for your job with eemont! I was thinking if there is possible to export a time series of an area of interest to pandas. After checking the documentation I think, that is only avaiable within a Geometry.Point. by getTimeSeriesByRegion function. Am I wrong or that is not possible with eemont? I want to have a pandas dataframe like this.
grafik

I need it to do the unsupervised classification with kmeans. Do you know how can I create such a dataframe with gee?
Thank you in advance! I'd be happy to work on it!

convert datetime in R

in the tutorial of timeseries by regions there is this line to convert date to datetime

tsPandas['date'] = pd.to_datetime(tsPandas['date'],infer_datetime_format = True)

I am trying to run it in R and getting this error:
Error in py_call_impl(callable, dots$args, dots$keywords) :
ValueError: to assemble mappings requires at least that [year, month, day] be specified: [day,month,year] is missing.

how can I solve this?

Moving to pydata sphinx format

  • Update sphinx format
  • Update classes documentation (update examples, notes and guides)
  • Create readthedocs workflow
  • Test docs

[JOSS Review] Package test automation/instructions

Looks like there is a set of tests that have been written for this package. Can you either automate these via some sort of continuous integration, or provide detailed instructions in the documentation so that an individual can run the tests locally?

I would consider adding a "developer installation" set of instructions to the documentation so that an aspiring contributor could follow steps to clone, install, locally test and make eventually make contributions to the project, but I leave that decision up to you.

Thanks.

This comment relates to the ongoing JOSS review of this package, comment pertains to the "Documentation" section of the JOSS reviewer guidelines:

Automated tests: Are there automated tests or manual steps described so that the functionality of the software can be verified?

[JOSS Review] Comments

It is clear this is a substantial scholarly effort and represents an excellent contribution to the remote sensing/scientific computing community. I don't think the paper/repo needs any substantial changes. It installed easily in a fresh virtual envronment via pip and conda and documentation was relatively straightforward. All code examples worked for me, installation was as documented, and the QGIS functionality worked as documented. I think it will be easy for GEE practitioners to implement.

Comments:

  • I do think the statement of need could be improved by stating the need first rather than stating what you've done first. I think this could be a single sentence or two clarifying that "The typical pre-processing and processing steps and long, and complex, making it challenging to move from data selection to analysis and these steps have been simplified with intelligent defaults and clearer more pythonic syntax." Just a template but I think that helps readers rapidly understand the main problem.
  • The tutorials are excellent, ran correctly in colab, and I learned a few nice tricks from them. I would recommend linking them in a clear spot in the README document instead of just on the docs.

Deprecation of index() and scale()

index() will be renamed as spectralIndices(), and scale() will be renamed as scaleAndOffset() to avoid confusion.

  • Add Deprecation Warnings
  • Add new methods
  • Add documentation

Add Extra module

The extra module will handle ee-extra methods that do not fit within specific Earth Engine classes modules.

[JOSS Review] Paper/documentation comments and suggestions

The eemont package is clearly a useful addition to the GEE API and improves the functionality and ease of use of Google Earth Engine, so congratulations to @davemlz for creating a useful package for the broader gee community.

That being said I think both the documentation and the JOSS paper could make a stronger case for the benefits of the eemont package. From the paper and documentation alone is not obvious to me why I would pick up eemont - potentially simpler code than the standard Earth Engine API? I would suggest showing the necessary code without eemont to perform the same analysis for one or two of the examples in the Features section of the documentation alongside the eemont implementation. Having a direct comparison of the (presumably longer) standard Earth Engine code required to accomplish the same task you can perform with eemont in just a few lines provides a much more compelling reason for someone who sees the package to use it.

In a similar vein, in the context of the paper, I think it would at least be beneficial to state the number of lines required to perform the process in the example given with and without eemont. This way a reader of the JOSS paper will be able to more intuitively grasp the value of using eemont. Even better, if there is some estimated "average" simplification or line-shortening accomplished by using eemont that'd be great to include, although I can understand that such a quantification may be impossible due to the diversity of remote sensing applications and processes eemont can be used for.

Regarding the "State of the field", I believe there are a handful of packages out there that provide additional functionality on-top of the standard Earth Engine API (e.g., geemap, geetools, some packages by GitHub users fitoprincipe and samapriya, just to highlight a few). I think the paper should mention more of these other tools and packages, as eemont can likely improve users' experiences with them as well (at least where calls to Earth Engine are made). Furthermore it would provide a good place to highlight the unique niche eemont fills amongst other tools and packages that build off of the standard Earth Engine API.


This comment relates to the ongoing JOSS review of this package, comment pertains to the "Documentation" and "Software paper" sections of the JOSS reviewer guidelines:

A statement of need: Do the authors clearly state what problems the software is designed to solve and who the target audience is?

A statement of need: Does the paper have a section titled 'Statement of Need' that clearly states what problems the software is designed to solve and who the target audience is?

State of the field: Do the authors describe how this software compares to other commonly-used packages?

Tasseled Cap Transformations

Is this a Feature Request for eemont or for eeExtra?
Not sure! It would require matching image STAC IDs to a list of ee.Image coefficients and calculating a weighted mean, so it could be done using just earthengine-api and your STAC tools from eemont.

Is your feature request related to a problem? Please describe.
If you're not familiar with tasseled cap transformations, different orthogonal axes (usually brightness, greenness, and wetness) are calculated as the weighted mean of spectral bands, and the weighting coefficients depend on the axis and the sensor. There are published coefficients available for Landsat OLI TOA, Landsat TM/ETM/OLI surface reflectance, Sentinel-2 MSI TOA, and MODIS.

Calculating tasseled cap manually in GEE is kind of a pain because it requires tracking down the correct coefficients, putting them into a multi-band constant image, and applying them to the matching bands in your input image. Having a method to quickly calculate tasseled cap in eemont would be awesome!

Describe the solution you'd like
ee.Image.tasseledCap() and ee.ImageCollection.tasseledCap() methods that would apply published band-wise coefficients to Landsat, MODIS, or Sentinel-2 imagery to calculate brightness, greenness, and wetness bands. Those bands could be returned as a new image or added to the original image.

Describe alternatives you've considered
I've thought about whether these could be implemented with your awesome-spectral-indices system, and I don't think that's possible because the formula varies depending on the dataset. But maybe there's a solution I haven't thought of?

Do you want to work on it?
Sure!

Additional context
:)

Image histogram matching

Building off of what was discussed in #32, this would be an ee.Image method for histogram matching adapted from this implementation by Noel Gorelick.

The user would specify the target image and two lists of matching bands for the source and target images. The output would be an image with the source bands histogram-matched to the target bands (and maybe a new property saying the image ID it was matched to.)

# Source image
l7 = ee.Image("LANDSAT/LE07/C01/T1_SR/LE07_044029_20140105")
# Target image
l8 = ee.Image("LANDSAT/LC08/C02/T1_L2/LC08_044029_20140113")

# R, G, B bands of source image
l7_bands = ["B3", "B2", "B1"]
# Corresponding bands of target image
l8_bands = ["B4", "B3", "B2"]

# Match the source image's histogram to the target image's histogram.
l7_matched  = l7.matchHistogram(target=l8, sourceBands=l7_bands, targetBands=l8_bands)

It should be possible to implement this for ee.ImageCollection, too, by matching each image in the collection to the target image and returning the collection, but I'm not sure how useful that would be. What do you think, @davemlz?

Feel free to assign me :)

EDIT: Actually, how about using a dictionary of bands instead of two lists? No chance of mismatched band numbers, less chance for mixing up bands, and the lists will be combined into a dictionary anyways to map over. Just a thought!

# Source image
l7 = ee.Image("LANDSAT/LE07/C01/T1_SR/LE07_044029_20140105")
# Target image
l8 = ee.Image("LANDSAT/LC08/C02/T1_L2/LC08_044029_20140113")

# R, G, B bands of source image (keys) and corresponding bands of the target image (values)
bands = {
  "B3": "B4", 
  "B2": "B3", 
  "B1": "B2"
}

# Match the source image's histogram to the target image's histogram.
l7_matched  = l7.matchHistogram(target=l8, bands=bands)

Update Algorithms for the Collection 2 of Landsat

Algorithms:

  • spectralIndices
  • index
  • scaleAndOffset (Automatic)
  • getScaleParams (Automatic)
  • getOffsetParams (Automatic)
  • getSTAC (Automatic)
  • getCitation (Automatic)
  • getDOI (Automatic)
  • maskClouds
  • preprocess (Automatic after updating scaleAndOffset and maskClouds)

Testing:

  • ee.Image
  • ee.ImageCollection

Docs:

  • Update Docs
  • Create tutorial

index() does not work when called within .map()

Hey!

First of all great work, very useful package. I am trying to create a time series dataset of indices based on Landsat collections. I have prepared a basic function that works well when it's run with one feature, but it fails when mapped to a feature collection. Here is a simplified version of the function:

def getStats(feature):
    oliCol = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR').filterBounds(feature.geometry()).filterDate('2013-01-01','2015-01-01')
    oliNDVI = oliCol.scale().index('NDVI').select('NDVI').toBands()
    zs = ee.Image(oliNDVI).reduceRegion(
        geometry = feature.geometry(),
        reducer = ee.Reducer.mean(),
        scale = 1000,
        tileScale = 4,
        bestEffort = True,
        maxPixels = 10e15
    )
    return feature.setMulti(zs)

This works:

res = getStats(aoi)

But this fails:

res = AOIs.map(getStats)

with the following error

image

Something is happening that prevents _get_platform from running properly. Is this expected? Any ideas how to fix?

Avoid the use of getInfo() and keep eemont on the server-side

The _get_platform function uses the getInfo() method to get the ID of an ee.Image (or ee.ImageCollection). This is used in order to compute methods such as index() (now renamed as spectralIndices()), scale() (now renamed as scaleAndOffset()) or maskClouds() for different platforms (datasets from the GEE Catalog) by using dictionaries with the ID of each platform.

The idea is to avoid the use of getInfo() and keep eemont on the server-side. How?

eemont scale and cloudMask error on data with end date before 2018-12-14

Hi I've found an apparent issue?

When using eemont for the cloudMask and scale convenience functions, if creating an image collection with a filterDate end date of before 2018-12-14 I receive the following error.

If I remove the .scale or .cloudMask from the call it completes, similarly if the end date is set to later than this it also completes. Is anyone else able to replicate this?

My suspicion is something has changed in the metadata info between these dates?


HttpError Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/ee/data.py in _execute_cloud_call(call, num_retries)
344 try:
--> 345 return call.execute(num_retries=num_retries)
346 except googleapiclient.errors.HttpError as e:

7 frames
HttpError: <HttpError 400 when requesting https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/value:compute?prettyPrint=false&alt=json returned "Element.get: Parameter 'object' is required.">

During handling of the above exception, another exception occurred:

EEException Traceback (most recent call last)
/usr/local/lib/python3.7/dist-packages/ee/data.py in _execute_cloud_call(call, num_retries)
345 return call.execute(num_retries=num_retries)
346 except googleapiclient.errors.HttpError as e:
--> 347 raise _translate_cloud_exception(e)
348
349

EEException: Element.get: Parameter 'object' is required.

Add `omega`

Add omega parameter for MBWI. Default = 2.

[JOSS Review] Errors in tutorial notebooks

Hello, I was running through the tutorial notebooks (locally) and ran into the following errors:

004-Computing-Spectral-Indices-Landsat-8.ipynb, I received the following error in the final cell:

---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
<ipython-input-16-e652de6f3203> in <module>
      2 Map.addLayer(L8.select('GNDVI'),visParamsGNDVI,'GNDVI')
      3 Map.addLayer(L8.select('NDWI'),visParamsNDWI,'NDWI')
----> 4 Map.add_colorbar(visParamsGNDVI['palette'], caption = 'GNDVI')
      5 Map.add_colorbar(visParamsNDWI['palette'], caption = 'NDWI')
      6 Map.addLayerControl()

~/anaconda3/envs/eemont/lib/python3.9/site-packages/geemap/geemap.py in add_colorbar(self, vis_params, cmap, discrete, label, orientation, position, transparent_bg, layer_name, **kwargs)
   2605 
   2606         if not isinstance(vis_params, dict):
-> 2607             raise TypeError("The vis_params must be a dictionary.")
   2608 
   2609         if orientation not in ["horizontal", "vertical"]:

TypeError: The vis_params must be a dictionary.

I had similar errors in tutorials 005 and 006 as well.


Then in 013-Time-Series-By-Region-Pandas.ipynb, I had the following code-error combo.
Code block:

tsPandas = pd.melt(tsPandas,
                   id_vars = ['system:index','reducer','date'],
                   value_vars = ['GNDVI','EVI'],
                   var_name = 'Index',
                   value_name = 'Value')

Error:

---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
<ipython-input-11-bd846c0b8d50> in <module>
----> 1 tsPandas = pd.melt(tsPandas,
      2                    id_vars = ['system:index','reducer','date'],
      3                    value_vars = ['GNDVI','EVI'],
      4                    var_name = 'Index',
      5                    value_name = 'Value')

~/anaconda3/envs/eemont/lib/python3.9/site-packages/pandas/core/reshape/melt.py in melt(frame, id_vars, value_vars, var_name, value_name, col_level, ignore_index)
     62             missing = Index(com.flatten(id_vars)).difference(cols)
     63             if not missing.empty:
---> 64                 raise KeyError(
     65                     "The following 'id_vars' are not present "
     66                     f"in the DataFrame: {list(missing)}"

KeyError: "The following 'id_vars' are not present in the DataFrame: ['system:index']"

I ran into a similar error in tutorial 014 as well.


I had no issues running tutorials 001, 002, or 003. For tutorials 007 and 008, I wasn't able to get the maps to load, but this may be an error (or impatience) on my end, as the "Loading widget..." text did come up.

This issue relates to the ongoing JOSS Review of this package.

Disable cron jobs on forks?

Describe the bug
Hey @davemlz! I was just updating my eemont fork and noticed it was ahead of the main fork by 60 commits just from automated STAC updates. It looks like the cron job workflows that are set up for eemont run independently on forks, which leads to a bunch of redundant commits. This isn't really a bug and doesn't actually cause any problems, but it does add some clutter to the commit history and eat some resources, so I thought I'd bring it to your attention in case you weren't aware :)

Additional context
I think this could be fixed by adding if: github.repository_owner == 'davemlz' to each of the workflow jobs. Here's a discussion of how another project fixed this and an example workflow.

ee.ImageCollection.getTimeSeriesByRegion breaks when specifying bands=None

Describe the bug
When choosing the bands as None as specified in the default and the documentation to get all images, the following error is thrown:

TypeError: object of type 'NoneType' has no len()

To Reproduce
Do not specify the bands parameter when using `ยดgetTimeSeriesByRegion``.

Setup (please complete the following information):

  • OS: Linux
  • python version: 3.8
  • eemont version: 0.2.5
  • earthengine-api version: 0.1.269

Additional context
Problem seems to be due to setNA using the bands parameter without checking for None.
Potential fixes could be changing setNA or settings the bands parameter to all bands when checking for None.

[JOSS Review] Comments and Suggestions

Repo:

  • Suggest removing all .ipynb_checkpoints. You might want to add .ipynb_checkpoints to .gitignore so that these unnecessary folders wonโ€™t be pushed to GitHub. (see pull request #21)
  • Suggest adding the docs website (https://eemont.readthedocs.io) to the About section located at the upper-right corner of the repo page.
  • Adding a Table of Content for README.rst, making it easier to navigate this long document.
  • In README.rst, the Contribution Steps section probably belongs to a subsection of Contributing.
  • You might want to consider publishing the package on conda-forge. Unless your package is available on conda-forge, other conda packages canโ€™t list eermont as a dependency. Check out my tutorial on publishing a conda package if needed.
  • The installation instructions can be improved by adding instructions such as how to update the package, install from GitHub, install from conda-forge (if it exists), etc.
  • For all notebooks, !pip install earthengine-api is not needed because eemont/geemap already has earthengine-api as a dependency.
  • Be cautious about using the GEE logo. Google is pretty picky about how their logos are used: https://www.google.com/permissions/logos-trademarks. I was warned before by a Google employee not to use the GEE logo. Unless you have received permission from Google to use/modify the GEE logo, you might want to design your own logo that is distinctive from the GEE logo.

Paper:

  • The writing can be improved. I made some minor changes. See pull request #21

Panchromatic image sharpening method

Hey @davemlz, what do you think about adding an ee.Image.panSharpen() method to eemont? I've implemented some sharpening algorithms before in Javascript, but it would be cool to have them accessible in Python. I think Landsat 7/8 are the only platforms on GEE with panchromatic bands, so it would probably only work with those collections. If you think that would be a good feature, I'd be happy to work on it!

Thanks!

Update GEE STAC IDs

  • Create a workflow to download the list.
  • Update the _get_platform method.
  • Update additional methods.

A .preprocess() method?

It would be nice to have a .preprocess() method for ee.Image and ee.ImageCollection object classes.

  • Mask clouds and shadows.
  • Scale and offset.

Emulating Containers

Containers emulation for:

  • ee.Image
  • ee.ImageCollection
  • ee.Feature
  • ee.FeatureCollection
  • ee.List
  • ee.Array (Not an EE object? Skip for now)
  • ee.Dictionary

Docs and Testing:

  • Update Docs
  • Testing

[JOSS Review] Contributor instructions

Hi, could you please add some instructions for contributors on how they might go about adding to this package? For example is there a particular code style, doc-string style that this package adheres to? Would you prefer issues opened before pull requests?

Thanks!

This comment relates to the ongoing JOSS review of this package, comment pertains to the "Documentation" section of the JOSS reviewer guidelines:

Community guidelines: Are there clear guidelines for third parties wishing to 1) Contribute to the software 2) Report issues or problems with the software 3) Seek support

Request to add drought indices in eemont

** Feature Request for eemont or ee extra**
Hello Dr David Congratulations for nice contribution to making easy use of GEE. I request to add in your eemont package few mode drought indices. I did not find way to fit distribution in GEE or in GGE related packages in python.

I request to add some more indices like standardized precipitation index(SPI).
The SPI calculation for any location is based on the long-term precipitation record for a desired period. This long-term record is fitted to a probability distribution, which is then transformed into a normal distribution so that the mean SPI for the location and desired period is zero (Edwards and McKee, 1997). Please find link of different drought indices https://www.droughtmanagement.info/indices/.
Thanks
P.K.Pandey

[Possible feature?] Points and polygons from plus codes

Hi @davemlz. First of all, eemont is a great tool. Thanks for creating it!

I'm not sure if you're familiar with open location code, but it's a geocoding tool that generates more readable coordinates (aka plus codes) from latitude/longitude (e.g. [39.777062, -105.006438] == 85FPQXGV+RC). Plus codes can also be shortened by including a nearby location (e.g. 85FPQXGV+RC == QXGV+RC Denver, CO).

It would be handy to have a tool for generating EE geometries from plus codes, and I was wondering if you think it would fit in eemont? Unfortunately there are no open web APIs that I know of to decode plus codes, so it would require adding openlocationcode as a dependency.

As far as implementation, I'm thinking it would extend ee.Geometry with a few static methods using your decorators. To handle shortened plus codes, an address could be geocoded using the same approach you use for ee.Geometry.PointFromQuery. Another feature might be the ability to retrieve a plus code from an ee.Geometry.Point. Usage might look something like below.

# Create a point from a full plus code
pt = ee.Geometry.PointFromPlusCode("85FPQXGV+RC")
# Create a point from a partial plus code and a nearby location reference
pt = ee.Geometry.PointFromPlusCode("QXGV+RC Denver, CO")
# Create a polygon from a list of plus codes
poly = ee.Geometry.PolygonFromPlusCodes(
    ["85GGXXJ2+R6"],
    ["85GVXVRV+5H"],
    ["859V2WJG+FQ"],
    ["859G2WQH+XF"]
)

# Create a point from coordinates
pt = ee.Geometry.Point([39.777062, -105.006438])
# Get the plus code of the point
pt.plus_code()

>> "85FPQXGV+RC"

If you think this would fit in eemont and it's worth adding another dependency for the feature, I'd be happy to implement and make a PR. If not, no worries! It could work as a little standalone package instead.

ee.Image.index raises AttributeError on median-reduced image

Hi @davemlz! I don't think this is really an eemont bug, but I'm wondering if you know of a good workaround or if maybe there's an eemont feature that could help with this.

Describe the bug
If you use a reducer other than ee.Reducer.first on an ImageCollection and then try to use the index method, an attribute error is thrown. I think this is because Earth Engine reducers like ee.Reducer.median don't preserve image metadata that's needed for _get_platform_STAC to work.

I'm not sure exactly what metadata _get_platform_STAC needs, but maybe I could fix this by setting or copying some properties after running median?

To Reproduce

import ee, eemont
ee.Initialize()

s2 = ee.ImageCollection("COPERNICUS/S2_SR")
pt = ee.Geometry.Point([-122.276437, 44.803428])
img = (s2
    .filterBounds(pt)
    .filterDate("2020-01-01", "2020-01-30")
    # Using .first() instead would fix the problem
    .median()
)

img = img.index("NDSI")
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_14264/966081019.py in <module>
     10 )
     11 
---> 12 img = img.index("NDSI")

~\anaconda3\envs\salvage\lib\site-packages\eemont\image.py in index(self, index, G, C1, C2, L, cexp, nexp, alpha, slope, intercept, kernel, sigma, p, c, online)
    746     )
    747 
--> 748     return _index(
    749         self,
    750         index,

~\anaconda3\envs\salvage\lib\site-packages\eemont\common.py in _index(self, index, G, C1, C2, L, cexp, nexp, alpha, slope, intercept, kernel, sigma, p, c, online)
    417         Image (or Image Collection) with the computed spectral index, or indices, as new bands.
    418     """
--> 419     platformDict = _get_platform_STAC(self)
    420 
    421     if isinstance(sigma, int) or isinstance(sigma, float):

~\anaconda3\envs\salvage\lib\site-packages\eemont\common.py in _get_platform_STAC(args)
     46             args, ee.image.Image
     47         ):
---> 48             pltID = "/".join(ID.split("/")[:-1])
     49         elif eeDict[platform]["gee:type"] == "image" and isinstance(
     50             args, ee.imagecollection.ImageCollection

AttributeError: 'NoneType' object has no attribute 'split'

Setup (please complete the following information):

  • OS: Windows 10
  • python version 3.9.6
  • eemont version 0.2.5
  • earthengine-api version 0.1.279

Additional context
Thanks!

Time Series By Region(s) is not working

eemont: 0.2.2
ee: 0.1.269

---------------------------------------------------------------------------
HttpError                                 Traceback (most recent call last)
~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/data.py in _execute_cloud_call(call, num_retries)
    333   try:
--> 334     return call.execute(num_retries=num_retries)
    335   except googleapiclient.errors.HttpError as e:

~/anaconda3/envs/gee/lib/python3.9/site-packages/googleapiclient/_helpers.py in positional_wrapper(*args, **kwargs)
    133                     logger.warning(message)
--> 134             return wrapped(*args, **kwargs)
    135 

~/anaconda3/envs/gee/lib/python3.9/site-packages/googleapiclient/http.py in execute(self, http, num_retries)
    914         if resp.status >= 300:
--> 915             raise HttpError(resp, content, uri=self.uri)
    916         return self.postproc(resp, content)

HttpError: <HttpError 400 when requesting https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/value:compute?prettyPrint=false&alt=json returned "ValueNode is empty". Details: "ValueNode is empty">

During handling of the above exception, another exception occurred:

EEException                               Traceback (most recent call last)
<ipython-input-7-6b769aefafc5> in <module>
----> 1 ts = S2.getTimeSeriesByRegion(geometry = roi,
      2                                   bands = indices,
      3                                   reducer = reducers,
      4                                   scale = scale)

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/imagecollection.py in getTimeSeriesByRegion(self, reducer, bands, geometry, scale, crs, crsTransform, bestEffort, maxPixels, tileScale, dateColumn, dateFormat, naValue)
    249 
    250         collections.append(
--> 251             ee.FeatureCollection(self.map(reduceImageCollectionByRegion))
    252         )
    253 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/collection.py in map(self, algorithm, opt_dropNulls)
    196     element_type = self.elementType()
    197     with_cast = lambda e: algorithm(element_type(e))
--> 198     return self._cast(apifunction.ApiFunction.call_(
    199         'Collection.map', self, with_cast, opt_dropNulls))
    200 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/apifunction.py in call_(cls, name, *args, **kwargs)
     79       a recognized return type, the returned value will be cast to that type.
     80     """
---> 81     return cls.lookup(name).call(*args, **kwargs)
     82 
     83   @classmethod

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/function.py in call(self, *args, **kwargs)
     65       to that type.
     66     """
---> 67     return self.apply(self.nameArgs(args, kwargs))
     68 
     69   def apply(self, named_args):

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/function.py in apply(self, named_args)
     78       to that type.
     79     """
---> 80     result = computedobject.ComputedObject(self, self.promoteArgs(named_args))
     81     return Function._promoter(result, self.getReturnType())
     82 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/function.py in promoteArgs(self, args)
    105       name = spec['name']
    106       if name in args:
--> 107         promoted_args[name] = Function._promoter(args[name], spec['type'])
    108       elif not spec.get('optional'):
    109         raise ee_exception.EEException(

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/__init__.py in _Promote(arg, klass)
    243       else:
    244         args_count = len(inspect.getfullargspec(arg).args)
--> 245       return CustomFunction.create(arg, 'Object', ['Object'] * args_count)
    246     elif isinstance(arg, Encodable):
    247       # An ee.Function or a computed function like the return value of

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/customfunction.py in create(func, return_type, arg_types)
    119         'args': args
    120     }
--> 121     return CustomFunction(signature, func)
    122 
    123   @staticmethod

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/customfunction.py in __init__(self, signature, body)
     41 
     42     # The signature of the function.
---> 43     self._signature = CustomFunction._resolveNamelessArgs(
     44         signature, variables, body)
     45 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/customfunction.py in _resolveNamelessArgs(signature, variables, body)
    164       return CountNodes(six.itervalues(expression['values']))
    165 
--> 166     serialized_body = serializer.encode(body(*variables), for_cloud_api=True)
    167     base_name = '_MAPPING_VAR_%d_' % CountFunctions(serialized_body)
    168 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/collection.py in <lambda>(e)
    195     """
    196     element_type = self.elementType()
--> 197     with_cast = lambda e: algorithm(element_type(e))
    198     return self._cast(apifunction.ApiFunction.call_(
    199         'Collection.map', self, with_cast, opt_dropNulls))

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/imagecollection.py in reduceImageCollectionByRegion(img)
    244             else:
    245                 date = ee.Date(img.get("system:time_start")).format(dateFormat)
--> 246             return ee.Feature(None, dictionary).set(
    247                 {dateColumn: date, "reducer": reducerName}
    248             )

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/computedobject.py in __call__(cls, *args, **kwargs)
     30       return args[0]
     31     else:
---> 32       return type.__call__(cls, *args, **kwargs)
     33 
     34 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/feature.py in __init__(self, geom, opt_properties)
     53       super(Feature, self).__init__(feature_constructor, {
     54           'geometry': geom,
---> 55           'metadata': opt_properties or None
     56       })
     57     elif isinstance(geom, computedobject.ComputedObject):

~/anaconda3/envs/gee/lib/python3.9/site-packages/eemont/dictionary.py in __len__(self)
     37         Length of the dictionary.
     38     '''
---> 39     return self.size().getInfo()
     40 
     41 

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/computedobject.py in getInfo(self)
     96       The object can evaluate to anything.
     97     """
---> 98     return data.computeValue(self)
     99 
    100   def encode(self, encoder):

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/data.py in computeValue(obj)
    672     The result of evaluating that object on the server.
    673   """
--> 674   return _execute_cloud_call(
    675       _get_cloud_api_resource().projects().value().compute(
    676           body={'expression': serializer.encode(obj, for_cloud_api=True)},

~/anaconda3/envs/gee/lib/python3.9/site-packages/ee/data.py in _execute_cloud_call(call, num_retries)
    334     return call.execute(num_retries=num_retries)
    335   except googleapiclient.errors.HttpError as e:
--> 336     raise _translate_cloud_exception(e)
    337 
    338 

EEException: ValueNode is empty

how to add a new formula into spectral-indices-dict.json file

Hello, I have a formula,called "WD", ("formula": "19.4260 + 9.2603 * (G / R) - 57.9165 * (R.log()) + 86.9934 * (G.log()) - 6.4577 * R - 17.6220 * (B.log())"), and added it into spectral-indices-dict.json file.

What I did:

f1 = ee.Feature(ee.Geometry.Point([120.076432, 30.86733]).buffer(10),{'ID':'A'})

fc = ee.FeatureCollection([f1])

S2 = (ee.ImageCollection('COPERNICUS/S2_SR')
.filterBounds(fc)
.filterDate('2021-04-30','2021-08-30')
#.filter(ee.Filter.lt('CLOUDY_PIXEL_PERCENTAGE', 40))
#.maskClouds()
.scale()
.index(['NDVI','GNDVI','EVI','WD']))

By Regions

ts = S2.getTimeSeriesByRegions(reducer = [ee.Reducer.mean()],
collection = fc,
bands = ['B2','B3','B4','B5','B6','B7','B8','B8A','WD','MSK_CLDPRB'],
scale = 10)

df = geemap.ee_to_pandas(ts, selectors=['B2','B3','B4','B5','B6','B7','B8','B8A','WD','MSK_CLDPRB'])

error
Exception: Image.parseExpression: Expression parse error at character 45:
'19.4260 + 9.2603 * (G / R) - 57.9165 * (R.log()) + 86.9934 * (G.log()) - 6.4577 * R - 17.6220 * (B.log())'
^.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.