8000 `ee.Dictionary` to `ee.FeatureCollection` by fitoprincipe · Pull Request #372 · gee-community/geetools · GitHub
[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
Skip to content

ee.Dictionary to ee.FeatureCollection #372

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Jan 14, 2025
121 changes: 121 additions & 0 deletions geetools/ee_dictionary.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Extra methods for the ``ee.Dictionary`` class."""
from __future__ import annotations

from typing import Literal

import ee

from .accessors import register_class_accessor
Expand Down Expand Up @@ -80,3 +82,122 @@ def getMany(self, list: list | ee.List) -> ee.List:
d.geetools.getMany(["foo", "bar"]).getInfo()
"""
return ee.List(list).map(lambda key: self._obj.get(key))

def toTable(
self, valueType: Literal["dict", "list", "value"] = "value"
) -> ee.FeatureCollection:
"""Convert a :py:class:`ee.Dictionary` to a :py:class:`ee.FeatureCollection` with no geometries (table).

There are 3 different type of values handled by this method:

1. value (default): when values are a :py:class:`ee.String` or
:py:class:`ee.Number`, the keys will be saved in the column
``system:index`` and the values in the column "value".

2. dict: when values are a :py:class:`ee.Dictionary`, the keys will be
saved in the column ``system:index`` and the values will be treated
as each Feature's properties.

3. list: when values are a :py:class:`ee.List` of numbers or strings,
the keys will be saved in the column ``system:index`` and the values
in as many columns as items in the list. The column name pattern is
"value_{i}" where i is the position of the element in the list.

These are the only supported patterns. Other patterns should be converted
to one of these. For example, the values of a reduction using the
reducer :py:meth:`ee.Reducer.frequencyHistogram` are of type
:py:class:`ee.Array` and the array contains lists.

Parameters:
valueType: this will define how to process the values.

Returns:
a collection in which the keys of the :py:class:`ee.Dictionary` are
in the ``system:index`` and the values are in new columns.

Examples:
.. jupyter-execute::

import ee, geetools
from geetools.utils import initialize_documentation

initialize_documentation()

d = ee.Dictionary({"foo": 1, "bar": 2})
d.geetools.toTable().getInfo()

.. jupyter-execute::

import ee, geetools
from geetools.utils import initialize_documentation

8000 initialize_documentation()

d = ee.Dictionary({
"Argentina": {"ADM0_CODE": 12, "Shape_Area": 278.289196625},
"Armenia": {"ADM0_CODE": 13, "Shape_Area": 3.13783139285},
})
d.geetools.toTable('dict').getInfo()

.. jupyter-execute::

import ee, geetools
from geetools.utils import initialize_documentation

initialize_documentation()

d = ee.Dictionary({
"Argentina": [12, 278.289196625],
"Armenia": [13, 3.13783139285],
})
d.geetools.toTable().getInfo()

.. jupyter-execute::

import ee, geetools
from geetools.utils import initialize_documentation

initialize_documentation()

# reduction
ran = ee.Image.random().multiply(10).reduceRegion(
reducer=ee.Reducer.fixedHistogram(0, 1.1, 11),
geometry=ee.Geometry.Point([0,0]).buffer(1000),
scale=100
)

# process to get desired format
res = ee.Array(ee.Dictionary(ran).get('random'))
reslist = res.toList()
keys = reslist.map(lambda i: ee.Number(ee.List(i).get(0)).multiply(100).toInt().format())
values = reslist.map(lambda i: ee.Number(ee.List(i).get(1)).toInt())
final = ee.Dictionary.fromLists(keys, values)

# fetch
final.geetools.toTable().getInfo()
"""

def features_from_dict(key, value) -> ee.Feature:
index = {"system:index": ee.String(key)}
props = ee.Dictionary(value).combine(index)
return ee.Feature(None, props)

def features_from_list(key, value) -> ee.Feature:
index = {"system:index": ee.String(key)}
values = ee.List(value)
columns = ee.List.sequence(0, values.size().subtract(1))
columns = columns.map(lambda k: ee.String("value_").cat(ee.Number(k).toInt()))
props = ee.Dictionary.fromLists(columns, values).combine(index)
return ee.Feature(None, props)

def features_from_any(key, value) -> ee.Feature:
props = {"system:index": ee.String(key), "value": value}
return ee.Feature(None, props)

make_features = {
"list": features_from_list,
"dict": features_from_dict,
"value": features_from_any,
}
features = self._obj.map(make_features[valueType]).values()
return ee.FeatureCollection(features)
26 changes: 26 additions & 0 deletions tests/test_Dictionary.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
"""Test the Dictionary class methods."""
import ee

import geetools # noqa: F401


class TestFromPairs:
"""Test the fromPairs method."""
Expand Down Expand Up @@ -28,3 +30,27 @@ class TestGetMany:
def test_getMany(self):
d = ee.Dictionary({"foo": 1, "bar": 2}).geetools.getMany(["foo"])
assert d.getInfo() == [1]


class TestToTable:
"""Test the `toTable` method."""

def test_to_table_any(self, data_regression):
ee_dict = ee.Dictionary({"foo": 1, "bar": 2})
res = ee_dict.geetools.toTable()
data_regression.check(res.getInfo())

def test_to_table_list(self, data_regression):
ee_dict = ee.Dictionary({"Argentina": [12, 278.289196625], "Armenia": [13, 3.13783139285]})
res = ee_dict.geetools.toTable("list")
data_regression.check(res.getInfo())

def test_to_table_dict(self, data_regression):
ee_dict = ee.Dictionary(
{
"Argentina": {"ADM0_CODE": 12, "Shape_Area": 278.289196625},
"Armenia": {"ADM0_CODE": 13, "Shape_Area": 3.13783139285},
}
)
res = ee_dict.geetools.toTable("dict")
data_regression.check(res.getInfo())
15 changes: 15 additions & 0 deletions tests/test_Dictionary/test_to_table_any.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
columns:
system:index: String
value: Integer
features:
- geometry: null
id: bar
properties:
value: 2
type: Feature
- geometry: null
id: foo
properties:
value: 1
type: Feature
type: FeatureCollection
18 changes: 18 additions & 0 deletions tests/test_Dictionary/test_to_table_dict.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
columns:
ADM0_CODE: Integer
Shape_Area: Float
system:index: String
features:
- geometry: null
id: Argentina
properties:
ADM0_CODE: 12
Shape_Area: 278.289196625
type: Feature
- geometry: null
id: Armenia
properties:
ADM0_CODE: 13
Shape_Area: 3.13783139285
type: Feature
type: FeatureCollection
18 changes: 18 additions & 0 deletions tests/test_Dictionary/test_to_table_list.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
columns:
system:index: String
value_0: Integer
value_1: Float
features:
- geometry: null
id: Argentina
properties:
value_0: 12
value_1: 278.289196625
type: Feature
- geometry: null
id: Armenia
properties:
value_0: 13
value_1: 3.13783139285
type: Feature
type: FeatureCollection
Loading
0