├── ee_jupyter
├── __init__.py
├── core.py
├── colab.py
├── layout.py
├── _modidx.py
└── ipyleaflet.py
├── sidebar.yml
├── custom.yml
├── MANIFEST.in
├── index_files
└── figure-commonmark
│ └── cell-10-output-1.png
├── nbs
├── sidebar.yml
├── nbdev.yml
├── _quarto.yml
├── 00_colab.ipynb
├── 00_core.ipynb
├── 02_layout.ipynb
├── index.ipynb
└── 01_ipyleaflet.ipynb
├── nbdev.yml
├── .github
└── workflows
│ ├── test.yaml
│ └── deploy.yaml
├── styles.css
├── settings.ini
├── docs
├── contributing.md
└── code-of-conduct.md
├── CHANGELOG.md
├── .gitignore
├── setup.py
├── README.md
└── LICENSE
/ee_jupyter/__init__.py:
--------------------------------------------------------------------------------
1 | __version__ = "0.0.7"
2 |
--------------------------------------------------------------------------------
/sidebar.yml:
--------------------------------------------------------------------------------
1 | website:
2 | sidebar:
3 | contents:
4 |
--------------------------------------------------------------------------------
/custom.yml:
--------------------------------------------------------------------------------
1 | website:
2 | repo-url: "https://github.com/google/earthengine-jupyter"
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include settings.ini
2 | include LICENSE
3 | include CONTRIBUTING.md
4 | include README.md
5 | recursive-exclude * __pycache__
6 |
--------------------------------------------------------------------------------
/index_files/figure-commonmark/cell-10-output-1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/google/earthengine-jupyter/HEAD/index_files/figure-commonmark/cell-10-output-1.png
--------------------------------------------------------------------------------
/nbs/sidebar.yml:
--------------------------------------------------------------------------------
1 | website:
2 | sidebar:
3 | contents:
4 | - index.ipynb
5 | - 00_colab.ipynb
6 | - 00_core.ipynb
7 | - 01_ipyleaflet.ipynb
8 | - 02_layout.ipynb
9 | - 03_rich_display.ipynb
--------------------------------------------------------------------------------
/nbs/nbdev.yml:
--------------------------------------------------------------------------------
1 | project:
2 | output-dir: _docs
3 |
4 | website:
5 | title: "earthengine-jupyter"
6 | site-url: "https://google.github.io/earthengine-jupyter"
7 | description: "Tools for working with the Earth Engine from a Jupyter development environment"
8 | repo-branch: main
9 | repo-url: "https://github.com/google/earthengine-jupyter"
10 |
--------------------------------------------------------------------------------
/nbdev.yml:
--------------------------------------------------------------------------------
1 | project:
2 | output-dir: _docs
3 |
4 | website:
5 | title: "earthengine-jupyter"
6 | site-url: "https://googlestaging.github.io/earthengine-jupyter"
7 | description: "Tools for working with the Earth Engine from a Jupyter development environment"
8 | repo-branch: main
9 | repo-url: "https://github.com/googlestaging/earthengine-jupyter"
10 |
--------------------------------------------------------------------------------
/nbs/_quarto.yml:
--------------------------------------------------------------------------------
1 | project:
2 | type: website
3 |
4 | format:
5 | html:
6 | theme: cosmo
7 | css: styles.css
8 | toc: true
9 |
10 | website:
11 | twitter-card: true
12 | open-graph: true
13 | repo-actions: [issue]
14 | navbar:
15 | background: primary
16 | search: true
17 | sidebar:
18 | style: floating
19 |
20 | metadata-files: [nbdev.yml, sidebar.yml]
--------------------------------------------------------------------------------
/ee_jupyter/core.py:
--------------------------------------------------------------------------------
1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
2 |
3 | # %% auto 0
4 | __all__ = ['authenticate_if_needed']
5 |
6 | # %% ../nbs/00_core.ipynb 5
7 | def authenticate_if_needed():
8 | """Authenticate Earth Engine, if credentials do not exist.
9 |
10 | * Authenticate, if needed."""
11 |
12 | import ee
13 | import os
14 |
15 | if os.path.exists(ee.oauth.get_credentials_path()):
16 | print('\N{check mark} Authentication credentials were found.')
17 | else:
18 | ee.Authenticate()
19 |
--------------------------------------------------------------------------------
/ee_jupyter/colab.py:
--------------------------------------------------------------------------------
1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_colab.ipynb.
2 |
3 | # %% auto 0
4 | __all__ = ['set_colab_output_cell_height']
5 |
6 | # %% ../nbs/00_colab.ipynb 3
7 | def set_colab_output_cell_height(max_height):
8 | """Sets the maximum height for the current notebook cell's output."""
9 | import google
10 |
11 | from IPython.display import Javascript
12 | from string import Template
13 | s = Template('google.colab.output.setIframeHeight(0, true, {maxHeight: $height})')
14 | display(Javascript(s.substitute(height=max_height)))
15 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 | on: [workflow_dispatch, pull_request, push]
3 |
4 | jobs:
5 | test:
6 | environment: CI
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Write Earth Engine credentials file
10 | run: |
11 | mkdir -p /home/runner/.config/earthengine
12 | ls -l /home/runner/.config/earthengine/
13 | echo ${{ secrets.SECRET_CREDENTIALS }} | base64 --decode > /home/runner/.config/earthengine/credentials
14 | ls -l /home/runner/.config/earthengine/
15 | - uses: fastai/workflows/nbdev-ci@master
16 | with: {pre: 1}
17 |
--------------------------------------------------------------------------------
/.github/workflows/deploy.yaml:
--------------------------------------------------------------------------------
1 | name: Deploy to GitHub Pages
2 | on:
3 | push:
4 | branches: [ "main", "master" ]
5 | workflow_dispatch:
6 | jobs:
7 | deploy:
8 | environment: CI
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Write Earth Engine credentials file
12 | run: |
13 | mkdir -p /home/runner/.config/earthengine
14 | ls -l /home/runner/.config/earthengine/
15 | echo ${{ secrets.SECRET_CREDENTIALS }} | base64 --decode > /home/runner/.config/earthengine/credentials
16 | ls -l /home/runner/.config/earthengine/
17 | - uses: fastai/workflows/quarto-ghp@master
18 | with: {pre: 1}
19 |
--------------------------------------------------------------------------------
/styles.css:
--------------------------------------------------------------------------------
1 | .cell {
2 | margin-bottom: 1rem;
3 | }
4 |
5 | .cell > .sourceCode {
6 | margin-bottom: 0;
7 | }
8 |
9 | .cell-output > pre {
10 | margin-bottom: 0;
11 | }
12 |
13 | .cell-output > pre, .cell-output > .sourceCode > pre, .cell-output-stdout > pre {
14 | margin-left: 0.8rem;
15 | margin-top: 0;
16 | background: none;
17 | border-left: 2px solid lightsalmon;
18 | border-top-left-radius: 0;
19 | border-top-right-radius: 0;
20 | }
21 |
22 | .cell-output > .sourceCode {
23 | border: none;
24 | }
25 |
26 | .cell-output > .sourceCode {
27 | background: none;
28 | margin-top: 0;
29 | }
30 |
31 | div.description {
32 | padding-left: 2px;
33 | padding-top: 5px;
34 | font-style: italic;
35 | font-size: 135%;
36 | opacity: 70%;
37 | }
38 |
--------------------------------------------------------------------------------
/settings.ini:
--------------------------------------------------------------------------------
1 | [DEFAULT]
2 | repo = earthengine-jupyter
3 | lib_name = earthengine-jupyter
4 | version = 0.0.7
5 | min_python = 3.7
6 | license = apache2
7 | doc_path = _docs
8 | lib_path = ee_jupyter
9 | nbs_path = nbs
10 | recursive = False
11 | tst_flags = notest
12 | branch = main
13 | custom_sidebar = False
14 | doc_host = https://google.github.io
15 | doc_baseurl = /earthengine-jupyter
16 | git_url = https://github.com/google/earthengine-jupyter
17 | title = earthengine-jupyter
18 | audience = Developers
19 | author = tylere
20 | author_email = tylere@google.com
21 | copyright = 2022 ownwards, tylere
22 | description = Tools for working with the Earth Engine from a Jupyter development environment
23 | keywords = nbdev jupyter notebook python
24 | language = English
25 | status = 3
26 | user = google
27 | requirements = earthengine-api jupyter ipyleaflet ipytree
28 | black_formatting = False
29 | readme_nb = index.ipynb
30 | jupyter_hooks = True
31 | allowed_metadata_keys = widgets
32 | allowed_cell_metadata_keys =
33 | clean_ids = True
34 | clear_all = False
35 | put_version_in_init = True
36 |
37 |
--------------------------------------------------------------------------------
/docs/contributing.md:
--------------------------------------------------------------------------------
1 | # How to Contribute
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Contributor License Agreement
7 |
8 | Contributions to this project must be accompanied by a Contributor License
9 | Agreement. You (or your employer) retain the copyright to your contribution;
10 | this simply gives us permission to use and redistribute your contributions as
11 | part of the project. Head over to to see
12 | your current agreements on file or to sign a new one.
13 |
14 | You generally only need to submit a CLA once, so if you've already submitted one
15 | (even if it was for a different project), you probably don't need to do it
16 | again.
17 |
18 | ## Code Reviews
19 |
20 | All submissions, including submissions by project members, require review. We
21 | use GitHub pull requests for this purpose. Consult
22 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
23 | information on using pull requests.
24 |
25 | ## Community Guidelines
26 |
27 | This project follows [Google's Open Source Community
28 | Guidelines](https://opensource.google/conduct/).
29 |
--------------------------------------------------------------------------------
/ee_jupyter/layout.py:
--------------------------------------------------------------------------------
1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_layout.ipynb.
2 |
3 | # %% auto 0
4 | __all__ = ['MapWithInspector']
5 |
6 | # %% ../nbs/02_layout.ipynb 4
7 | from .ipyleaflet import Map
8 | from .ipyleaflet import Inspector
9 | import ee
10 | import ipyleaflet
11 | import ipywidgets as widgets
12 | import logging
13 |
14 | # %% ../nbs/02_layout.ipynb 8
15 | class MapWithInspector(widgets.HBox):
16 | """A map with a tobbleable inspector."""
17 |
18 | # Font Awesome icons: https://fontawesome.com/v4/examples/
19 | ICON_INSPECTOR_SHOWN = 'info-circle 2x fw inverse'
20 | ICON_INSPECTOR_HIDDEN = 'info-circle 2x fw'
21 |
22 | def __init__(self, **kwargs):
23 |
24 | self.map = Map(**kwargs)
25 |
26 | button_inspector_toggle = widgets.Button(
27 | icon = self.ICON_INSPECTOR_SHOWN,
28 | layout = widgets.Layout(width='45px', height='45px', padding='0px')
29 | )
30 |
31 | def show_inspector(visible):
32 | if visible:
33 | self.inspector.layout.width = None
34 | self.inspector.layout.min_width = f'300px'
35 | self.inspector.disabled = False
36 | else:
37 | self.inspector.layout.width = f'0%'
38 | self.inspector.layout.min_width = None
39 | self.inspector.disabled = True
40 |
41 | def on_button_clicked(_b):
42 | if _b.icon == self.ICON_INSPECTOR_HIDDEN:
43 | show_inspector(False)
44 | _b.icon = self.ICON_INSPECTOR_SHOWN
45 | else:
46 | show_inspector(True)
47 | _b.icon = self.ICON_INSPECTOR_HIDDEN
48 | button_inspector_toggle.on_click(on_button_clicked)
49 |
50 | widget_control1 = ipyleaflet.WidgetControl(widget=button_inspector_toggle,
51 | position='topright')
52 | self.map.add_control(widget_control1)
53 | self.inspector = Inspector(self.map)
54 |
55 | kwargs['children'] = [self.map, self.inspector]
56 | show_inspector(False)
57 |
58 | super().__init__(**kwargs)
59 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Release notes
2 |
3 |
4 |
5 | ## 0.0.7
6 |
7 | - Corrected nbdev settings (GitHub repo owner)
8 |
9 |
10 | ## 0.0.6
11 |
12 | ### New Features
13 |
14 | - Make map-inspector layout panels resizable ([#22](https://github.com/google/earthengine-jupyter/issues/22))
15 |
16 |
17 | ## 0.0.4
18 |
19 | ### New Features
20 |
21 | - Inspector: display scrollbar when exceeding maximum height or width ([#11](https://github.com/tylere/earthengine-jupyter/issues/11))
22 |
23 | - Inspector: Simplify the object display by using OrderedDict ([#10](https://github.com/tylere/earthengine-jupyter/issues/10))
24 | - The object folder's display logic currently hardcodes the keys that it expects to display. It could be better to order the keys that are returned by ee.Object.getInfo().
25 |
26 | - Map: Display crosshairs when cursor is over the map ([#9](https://github.com/tylere/earthengine-jupyter/issues/9))
27 |
28 | - Provide map view details in the Inspector's Point folder ([#5](https://github.com/tylere/earthengine-jupyter/issues/5))
29 |
30 | - Inspector: return "No unmasked pixels at clicked point." for layers with fully masked bands ([#2](https://github.com/tylere/earthengine-jupyter/issues/2))
31 |
32 | - Inspector: disable Pixels & Objects folders if there are non map layers ([#1](https://github.com/tylere/earthengine-jupyter/issues/1))
33 | - Similar to the Code Editor, only display the Pixels and Objects folders if the map has layers
34 |
35 |
36 | ## 0.0.3
37 |
38 |
39 | ### Bugs Squashed
40 |
41 | - Having code in package to install itself doesn't work ([#12](https://github.com/google/earthengine-jupyter/issues/12))
42 |
43 |
44 | ## 0.0.2
45 |
46 | ### New Features
47 |
48 | - Add colab setup function ([#11](https://github.com/google/earthengine-jupyter/issues/11))
49 | - Add a function to simplify setting up Colab (install packages, authenticate).
50 |
51 |
52 |
53 | ## 0.0.1
54 |
55 | ### New Features
56 |
57 | - Rename library to be more consistent with Earth Engine client library ([#3](https://github.com/google/earthengine-jupyter/issues/3))
58 | - earthengine_jupyter -> ee_jupyter
59 |
60 | - Create initial content ([#2](https://github.com/google/earthengine-jupyter/issues/2))
61 | - An initial issue intended to exercising the changelog, release, and push to PyPI.
62 |
63 |
64 |
65 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | _docs/
2 | _proc/
3 |
4 | *.bak
5 | .gitattributes
6 | .last_checked
7 | .gitconfig
8 | *.bak
9 | *.log
10 | *~
11 | ~*
12 | _tmp*
13 | tmp*
14 | tags
15 | *.pkg
16 |
17 | # Byte-compiled / optimized / DLL files
18 | __pycache__/
19 | *.py[cod]
20 | *$py.class
21 |
22 | # C extensions
23 | *.so
24 |
25 | # Distribution / packaging
26 | .Python
27 | env/
28 | build/
29 | develop-eggs/
30 | dist/
31 | downloads/
32 | eggs/
33 | .eggs/
34 | lib/
35 | lib64/
36 | parts/
37 | sdist/
38 | var/
39 | wheels/
40 | *.egg-info/
41 | .installed.cfg
42 | *.egg
43 |
44 | # PyInstaller
45 | # Usually these files are written by a python script from a template
46 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
47 | *.manifest
48 | *.spec
49 |
50 | # Installer logs
51 | pip-log.txt
52 | pip-delete-this-directory.txt
53 |
54 | # Unit test / coverage reports
55 | htmlcov/
56 | .tox/
57 | .coverage
58 | .coverage.*
59 | .cache
60 | nosetests.xml
61 | coverage.xml
62 | *.cover
63 | .hypothesis/
64 |
65 | # Translations
66 | *.mo
67 | *.pot
68 |
69 | # Django stuff:
70 | *.log
71 | local_settings.py
72 |
73 | # Flask stuff:
74 | instance/
75 | .webassets-cache
76 |
77 | # Scrapy stuff:
78 | .scrapy
79 |
80 | # Sphinx documentation
81 | docs/_build/
82 |
83 | # PyBuilder
84 | target/
85 |
86 | # Jupyter Notebook
87 | .ipynb_checkpoints
88 |
89 | # pyenv
90 | .python-version
91 |
92 | # celery beat schedule file
93 | celerybeat-schedule
94 |
95 | # SageMath parsed files
96 | *.sage.py
97 |
98 | # dotenv
99 | .env
100 |
101 | # virtualenv
102 | .venv
103 | venv/
104 | ENV/
105 |
106 | # Spyder project settings
107 | .spyderproject
108 | .spyproject
109 |
110 | # Rope project settings
111 | .ropeproject
112 |
113 | # mkdocs documentation
114 | /site
115 |
116 | # mypy
117 | .mypy_cache/
118 |
119 | .vscode
120 | *.swp
121 |
122 | # osx generated files
123 | .DS_Store
124 | .DS_Store?
125 | .Trashes
126 | ehthumbs.db
127 | Thumbs.db
128 | .idea
129 |
130 | # pytest
131 | .pytest_cache
132 |
133 | # tools/trust-doc-nbs
134 | docs_src/.last_checked
135 |
136 | # symlinks to fastai
137 | docs_src/fastai
138 | tools/fastai
139 |
140 | # link checker
141 | checklink/cookies.txt
142 |
143 | # .gitconfig is now autogenerated
144 | .gitconfig
145 |
146 | # Quarto installer
147 | .deb
148 | .pkg
149 |
150 | # Quarto
151 | .quarto
152 |
153 | /.quarto/
154 | token
155 | deps/
156 | token
157 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from pkg_resources import parse_version
2 | from configparser import ConfigParser
3 | import setuptools
4 | assert parse_version(setuptools.__version__)>=parse_version('36.2')
5 |
6 | # note: all settings are in settings.ini; edit there, not here
7 | config = ConfigParser(delimiters=['='])
8 | config.read('settings.ini')
9 | cfg = config['DEFAULT']
10 |
11 | cfg_keys = 'version description keywords author author_email'.split()
12 | expected = cfg_keys + "lib_name user branch license status min_python audience language".split()
13 | for o in expected: assert o in cfg, "missing expected setting: {}".format(o)
14 | setup_cfg = {o:cfg[o] for o in cfg_keys}
15 |
16 | licenses = {
17 | 'apache2': ('Apache Software License 2.0','OSI Approved :: Apache Software License'),
18 | 'mit': ('MIT License', 'OSI Approved :: MIT License'),
19 | 'gpl2': ('GNU General Public License v2', 'OSI Approved :: GNU General Public License v2 (GPLv2)'),
20 | 'gpl3': ('GNU General Public License v3', 'OSI Approved :: GNU General Public License v3 (GPLv3)'),
21 | 'bsd3': ('BSD License', 'OSI Approved :: BSD License'),
22 | }
23 | statuses = [ '1 - Planning', '2 - Pre-Alpha', '3 - Alpha',
24 | '4 - Beta', '5 - Production/Stable', '6 - Mature', '7 - Inactive' ]
25 | py_versions = '3.6 3.7 3.8 3.9 3.10'.split()
26 |
27 | requirements = cfg.get('requirements','').split()
28 | if cfg.get('pip_requirements'): requirements += cfg.get('pip_requirements','').split()
29 | min_python = cfg['min_python']
30 | lic = licenses.get(cfg['license'].lower(), (cfg['license'], None))
31 | dev_requirements = (cfg.get('dev_requirements') or '').split()
32 |
33 | setuptools.setup(
34 | name = cfg['lib_name'],
35 | license = lic[0],
36 | classifiers = [
37 | 'Development Status :: ' + statuses[int(cfg['status'])],
38 | 'Intended Audience :: ' + cfg['audience'].title(),
39 | 'Natural Language :: ' + cfg['language'].title(),
40 | ] + ['Programming Language :: Python :: '+o for o in py_versions[py_versions.index(min_python):]] + (['License :: ' + lic[1] ] if lic[1] else []),
41 | url = cfg['git_url'],
42 | packages = setuptools.find_packages(),
43 | include_package_data = True,
44 | install_requires = requirements,
45 | extras_require={ 'dev': dev_requirements },
46 | dependency_links = cfg.get('dep_links','').split(),
47 | python_requires = '>=' + cfg['min_python'],
48 | long_description = open('README.md').read(),
49 | long_description_content_type = 'text/markdown',
50 | zip_safe = False,
51 | entry_points = {
52 | 'console_scripts': cfg.get('console_scripts','').split(),
53 | 'nbdev': [f'{cfg.get("lib_path")}={cfg.get("lib_path")}._modidx:d']
54 | },
55 | **setup_cfg)
56 |
57 |
58 |
--------------------------------------------------------------------------------
/nbs/00_colab.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "id": "05d86507-c00c-4f70-96a5-e47b56eb54e7",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "#|hide\n",
11 | "# Copyright 2022 Google LLC\n",
12 | "#\n",
13 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
14 | "# you may not use this file except in compliance with the License.\n",
15 | "# You may obtain a copy of the License at\n",
16 | "# \n",
17 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
18 | "# \n",
19 | "# Unless required by applicable law or agreed to in writing, software\n",
20 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
21 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
22 | "# See the License for the specific language governing permissions and\n",
23 | "# limitations under the License."
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "id": "5427930d-da44-425f-adac-6a9a2ca769e5",
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "#| default_exp colab"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "85662442-f26a-400f-9656-005d342855de",
39 | "metadata": {},
40 | "source": [
41 | "# Colab module\n",
42 | "\n",
43 | "> Tools specific to working within a Colab environment."
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "id": "e682b176-cf69-4cf2-9d4c-d2acc18f93b3",
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "#|export\n",
54 | "def set_colab_output_cell_height(max_height):\n",
55 | " \"\"\"Sets the maximum height for the current notebook cell's output.\"\"\"\n",
56 | " import google\n",
57 | " \n",
58 | " from IPython.display import Javascript\n",
59 | " from string import Template\n",
60 | " s = Template('google.colab.output.setIframeHeight(0, true, {maxHeight: $height})')\n",
61 | " display(Javascript(s.substitute(height=max_height)))"
62 | ]
63 | },
64 | {
65 | "cell_type": "code",
66 | "execution_count": null,
67 | "id": "e6cb06b8-a5ee-4f12-bab6-a292581be7de",
68 | "metadata": {},
69 | "outputs": [],
70 | "source": [
71 | "#|eval: false\n",
72 | "set_output_cell_height(max_height=200)"
73 | ]
74 | }
75 | ],
76 | "metadata": {
77 | "kernelspec": {
78 | "display_name": "Python 3 (ipykernel)",
79 | "language": "python",
80 | "name": "python3"
81 | },
82 | "widgets": {
83 | "application/vnd.jupyter.widget-state+json": {
84 | "state": {},
85 | "version_major": 2,
86 | "version_minor": 0
87 | }
88 | }
89 | },
90 | "nbformat": 4,
91 | "nbformat_minor": 5
92 | }
93 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | earthengine-jupyter
2 | ================
3 |
4 |
5 |
6 | **NOTICE: This is an experimental project and is not an officially
7 | supported Google project. You are welcome to use it, but we do not
8 | guarantee stability.**
9 |
10 | ## Setup
11 |
12 | ``` python
13 | try:
14 | import ee_jupyter
15 | print('ee_jupyter was already installed.')
16 | except ModuleNotFoundError:
17 | print('ee_jupyter was not found. Installing now...')
18 | import os
19 | result = os.system('pip -q install earthengine-jupyter')
20 | ```
21 |
22 | ## How to use
23 |
24 | This lib contains a
25 | [`Map`](https://google.github.io/earthengine-jupyter/ipyleaflet.html#map)
26 | class that can be used to display an interactive map.
27 |
28 | ``` python
29 | import ee
30 | from ee_jupyter.core import authenticate_if_needed
31 | from ee_jupyter.ipyleaflet import Map
32 | from ee_jupyter.layout import MapWithInspector
33 | import ipywidgets as widgets
34 | ```
35 |
36 | ``` python
37 | authenticate_if_needed()
38 | ```
39 |
40 | ``` python
41 | # Intialize the Earth Engine client library.
42 | ee.Initialize()
43 | ```
44 |
45 | ``` python
46 | map1 = Map(center=(37.5924, -122.09), zoom=8)
47 | map1
48 | ```
49 |
50 | Define an Earth Engine image layer, and add it to the interactive map.
51 |
52 | ``` python
53 | img1 = ee.Image("LANDSAT/LC09/C02/T1_L2/LC09_044034_20220127")
54 | visualization = {
55 | 'bands': ['SR_B4', 'SR_B3', 'SR_B2'],
56 | 'min': 0.2 / 0.0000275,
57 | 'max': 0.4 / 0.0000275,
58 | }
59 | map1.addLayer(eeObject=img1, visParams=visualization, name='Landsat scene')
60 | ```
61 |
62 | We can also create an inspector object and associate it with the
63 | previously created map.
64 |
65 | ``` python
66 | from ee_jupyter.ipyleaflet import Inspector
67 |
68 | inspector1 = Inspector(map_object=map1)
69 | inspector1
70 | ```
71 |
72 | Typically when you create a inspector object, you will want to display
73 | it with the map. The `MapWithInpsector` object adds a button that
74 | toggles the inspector functionality.
75 |
76 | The map below shows a Sentinel-2 image covering Paris. Click on the
77 | inspector toggle button to open the inspector.
78 |
79 | ``` python
80 | map_init_paris = {'center':(49.4, 2.3), 'zoom':8}
81 | m = MapWithInspector(**map_init_paris)
82 | image = ee.Image('COPERNICUS/S2_SR_HARMONIZED/20220604T104619_20220604T104620_T31UDQ')
83 | m.map.addLayer(image, {'bands': ['B4', 'B3', 'B2'], 'max': 2500}, 'Landsat image')
84 | m
85 | ```
86 |
87 |
88 |
89 | > **Tip With Caption**
90 | >
91 | > Note that when viewed on GitHub Pages you can manipulate Jupyter
92 | > widgets independently, but the widgets do not interact with each
93 | > other. To experience the cross-widget interactivity, open up this
94 | > notebook in a Jupyter environment.
95 |
96 |
97 |
98 | # Displaying a Map Image
99 |
100 | If you want to display a static (non-interactive) image, you can do that
101 | as well. The `embed=True` parameter will allow the image to be saved
102 | within the notebook.
103 |
104 | ``` python
105 | from IPython.display import Image
106 |
107 |
108 | visualization['dimensions'] = 400 # maximum dimension for the image
109 | url = img1.getThumbUrl(visualization)
110 |
111 | Image(url=url, format='png', embed=True)
112 | ```
113 |
--------------------------------------------------------------------------------
/nbs/00_core.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "id": "a299e0c1-82bf-4290-aab9-38397b05b533",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "#|hide\n",
11 | "# Copyright 2022 Google LLC\n",
12 | "#\n",
13 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
14 | "# you may not use this file except in compliance with the License.\n",
15 | "# You may obtain a copy of the License at\n",
16 | "# \n",
17 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
18 | "# \n",
19 | "# Unless required by applicable law or agreed to in writing, software\n",
20 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
21 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
22 | "# See the License for the specific language governing permissions and\n",
23 | "# limitations under the License."
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "id": "93bd9280-7fdc-47b3-9ab6-e5892cbc8a4b",
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "#| default_exp core"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "00063c01-2b45-4a56-b50a-aa20d10f4803",
39 | "metadata": {},
40 | "source": [
41 | "# core module\n",
42 | "\n",
43 | "> Core tools for working in Jupyter environments."
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "id": "3d11593e-2715-4345-8831-2b1ceccac939",
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "#|hide\n",
54 | "from nbdev.showdoc import *"
55 | ]
56 | },
57 | {
58 | "cell_type": "markdown",
59 | "id": "0a1fbd0b-4b5d-493b-bda2-9aa0603da7d1",
60 | "metadata": {},
61 | "source": [
62 | "## Tools"
63 | ]
64 | },
65 | {
66 | "cell_type": "code",
67 | "execution_count": null,
68 | "id": "c34615c6-17a5-476e-89ff-9b195f8564ed",
69 | "metadata": {},
70 | "outputs": [],
71 | "source": [
72 | "#|export\n",
73 | "def authenticate_if_needed():\n",
74 | " \"\"\"Authenticate Earth Engine, if credentials do not exist.\n",
75 | "\n",
76 | " * Authenticate, if needed.\"\"\"\n",
77 | "\n",
78 | " import ee\n",
79 | " import os\n",
80 | "\n",
81 | " if os.path.exists(ee.oauth.get_credentials_path()):\n",
82 | " print('\\N{check mark} Authentication credentials were found.')\n",
83 | " else:\n",
84 | " ee.Authenticate()"
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "id": "7f1a180e-2348-4d3b-9b87-3d1c7471713c",
91 | "metadata": {},
92 | "outputs": [
93 | {
94 | "name": "stdout",
95 | "output_type": "stream",
96 | "text": [
97 | "✓ Authentication credentials were found.\n"
98 | ]
99 | }
100 | ],
101 | "source": [
102 | "authenticate_if_needed()"
103 | ]
104 | },
105 | {
106 | "cell_type": "code",
107 | "execution_count": null,
108 | "id": "a36ef3eb-7720-47cc-92e4-23faa6141baf",
109 | "metadata": {},
110 | "outputs": [],
111 | "source": [
112 | "#| hide\n",
113 | "import nbdev; nbdev.nbdev_export()"
114 | ]
115 | }
116 | ],
117 | "metadata": {
118 | "kernelspec": {
119 | "display_name": "Python 3 (ipykernel)",
120 | "language": "python",
121 | "name": "python3"
122 | },
123 | "widgets": {
124 | "application/vnd.jupyter.widget-state+json": {
125 | "state": {},
126 | "version_major": 2,
127 | "version_minor": 0
128 | }
129 | }
130 | },
131 | "nbformat": 4,
132 | "nbformat_minor": 5
133 | }
134 |
--------------------------------------------------------------------------------
/docs/code-of-conduct.md:
--------------------------------------------------------------------------------
1 | # Code of Conduct
2 |
3 | ## Our Pledge
4 |
5 | In the interest of fostering an open and welcoming environment, we as
6 | contributors and maintainers pledge to making participation in our project and
7 | our community a harassment-free experience for everyone, regardless of age, body
8 | size, disability, ethnicity, gender identity and expression, level of
9 | experience, education, socio-economic status, nationality, personal appearance,
10 | race, religion, or sexual identity and orientation.
11 |
12 | ## Our Standards
13 |
14 | Examples of behavior that contributes to creating a positive environment
15 | include:
16 |
17 | * Using welcoming and inclusive language
18 | * Being respectful of differing viewpoints and experiences
19 | * Gracefully accepting constructive criticism
20 | * Focusing on what is best for the community
21 | * Showing empathy towards other community members
22 |
23 | Examples of unacceptable behavior by participants include:
24 |
25 | * The use of sexualized language or imagery and unwelcome sexual attention or
26 | advances
27 | * Trolling, insulting/derogatory comments, and personal or political attacks
28 | * Public or private harassment
29 | * Publishing others' private information, such as a physical or electronic
30 | address, without explicit permission
31 | * Other conduct which could reasonably be considered inappropriate in a
32 | professional setting
33 |
34 | ## Our Responsibilities
35 |
36 | Project maintainers are responsible for clarifying the standards of acceptable
37 | behavior and are expected to take appropriate and fair corrective action in
38 | response to any instances of unacceptable behavior.
39 |
40 | Project maintainers have the right and responsibility to remove, edit, or reject
41 | comments, commits, code, wiki edits, issues, and other contributions that are
42 | not aligned to this Code of Conduct, or to ban temporarily or permanently any
43 | contributor for other behaviors that they deem inappropriate, threatening,
44 | offensive, or harmful.
45 |
46 | ## Scope
47 |
48 | This Code of Conduct applies both within project spaces and in public spaces
49 | when an individual is representing the project or its community. Examples of
50 | representing a project or community include using an official project e-mail
51 | address, posting via an official social media account, or acting as an appointed
52 | representative at an online or offline event. Representation of a project may be
53 | further defined and clarified by project maintainers.
54 |
55 | This Code of Conduct also applies outside the project spaces when the Project
56 | Steward has a reasonable belief that an individual's behavior may have a
57 | negative impact on the project or its community.
58 |
59 | ## Conflict Resolution
60 |
61 | We do not believe that all conflict is bad; healthy debate and disagreement
62 | often yield positive results. However, it is never okay to be disrespectful or
63 | to engage in behavior that violates the project’s code of conduct.
64 |
65 | If you see someone violating the code of conduct, you are encouraged to address
66 | the behavior directly with those involved. Many issues can be resolved quickly
67 | and easily, and this gives people more control over the outcome of their
68 | dispute. If you are unable to resolve the matter for any reason, or if the
69 | behavior is threatening or harassing, report it. We are dedicated to providing
70 | an environment where participants feel welcome and safe.
71 |
72 | Reports should be directed to *[PROJECT STEWARD NAME(s) AND EMAIL(s)]*, the
73 | Project Steward(s) for *[PROJECT NAME]*. It is the Project Steward’s duty to
74 | receive and address reported violations of the code of conduct. They will then
75 | work with a committee consisting of representatives from the Open Source
76 | Programs Office and the Google Open Source Strategy team. If for any reason you
77 | are uncomfortable reaching out to the Project Steward, please email
78 | opensource@google.com.
79 |
80 | We will investigate every complaint, but you may not receive a direct response.
81 | We will use our discretion in determining when and how to follow up on reported
82 | incidents, which may range from not taking action to permanent expulsion from
83 | the project and project-sponsored spaces. We will notify the accused of the
84 | report and provide them an opportunity to discuss it before any action is taken.
85 | The identity of the reporter will be omitted from the details of the report
86 | supplied to the accused. In potentially harmful situations, such as ongoing
87 | harassment or threats to anyone's safety, we may take action without notice.
88 |
89 | ## Attribution
90 |
91 | This Code of Conduct is adapted from the Contributor Covenant, version 1.4,
92 | available at
93 | https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
94 |
--------------------------------------------------------------------------------
/ee_jupyter/_modidx.py:
--------------------------------------------------------------------------------
1 | # Autogenerated by nbdev
2 |
3 | d = { 'settings': { 'branch': 'main',
4 | 'doc_baseurl': '/earthengine-jupyter',
5 | 'doc_host': 'https://google.github.io',
6 | 'git_url': 'https://github.com/google/earthengine-jupyter',
7 | 'lib_path': 'ee_jupyter'},
8 | 'syms': { 'ee_jupyter.colab': { 'ee_jupyter.colab.set_colab_output_cell_height': ( 'colab.html#set_colab_output_cell_height',
9 | 'ee_jupyter/colab.py')},
10 | 'ee_jupyter.core': {'ee_jupyter.core.authenticate_if_needed': ('core.html#authenticate_if_needed', 'ee_jupyter/core.py')},
11 | 'ee_jupyter.ipyleaflet': { 'ee_jupyter.ipyleaflet.Inspector': ('ipyleaflet.html#inspector', 'ee_jupyter/ipyleaflet.py'),
12 | 'ee_jupyter.ipyleaflet.Inspector.__init__': ( 'ipyleaflet.html#inspector.__init__',
13 | 'ee_jupyter/ipyleaflet.py'),
14 | 'ee_jupyter.ipyleaflet.Inspector.disabled': ( 'ipyleaflet.html#inspector.disabled',
15 | 'ee_jupyter/ipyleaflet.py'),
16 | 'ee_jupyter.ipyleaflet.Inspector.get_map': ( 'ipyleaflet.html#inspector.get_map',
17 | 'ee_jupyter/ipyleaflet.py'),
18 | 'ee_jupyter.ipyleaflet.Inspector.objects_node': ( 'ipyleaflet.html#inspector.objects_node',
19 | 'ee_jupyter/ipyleaflet.py'),
20 | 'ee_jupyter.ipyleaflet.Inspector.pixels_node': ( 'ipyleaflet.html#inspector.pixels_node',
21 | 'ee_jupyter/ipyleaflet.py'),
22 | 'ee_jupyter.ipyleaflet.Inspector.point_node': ( 'ipyleaflet.html#inspector.point_node',
23 | 'ee_jupyter/ipyleaflet.py'),
24 | 'ee_jupyter.ipyleaflet.Inspector.register_map': ( 'ipyleaflet.html#inspector.register_map',
25 | 'ee_jupyter/ipyleaflet.py'),
26 | 'ee_jupyter.ipyleaflet.Inspector.set_map': ( 'ipyleaflet.html#inspector.set_map',
27 | 'ee_jupyter/ipyleaflet.py'),
28 | 'ee_jupyter.ipyleaflet.Inspector.update_inspector': ( 'ipyleaflet.html#inspector.update_inspector',
29 | 'ee_jupyter/ipyleaflet.py'),
30 | 'ee_jupyter.ipyleaflet.Map': ('ipyleaflet.html#map', 'ee_jupyter/ipyleaflet.py'),
31 | 'ee_jupyter.ipyleaflet.Map.__init__': ('ipyleaflet.html#map.__init__', 'ee_jupyter/ipyleaflet.py'),
32 | 'ee_jupyter.ipyleaflet.Map.addLayer': ('ipyleaflet.html#map.addlayer', 'ee_jupyter/ipyleaflet.py'),
33 | 'ee_jupyter.ipyleaflet.StructureTree': ('ipyleaflet.html#structuretree', 'ee_jupyter/ipyleaflet.py'),
34 | 'ee_jupyter.ipyleaflet.StructureTree.__init__': ( 'ipyleaflet.html#structuretree.__init__',
35 | 'ee_jupyter/ipyleaflet.py'),
36 | 'ee_jupyter.ipyleaflet.StructureTree.__ipytreeify': ( 'ipyleaflet.html#structuretree.__ipytreeify',
37 | 'ee_jupyter/ipyleaflet.py'),
38 | 'ee_jupyter.ipyleaflet.TileLayerEE': ('ipyleaflet.html#tilelayeree', 'ee_jupyter/ipyleaflet.py'),
39 | 'ee_jupyter.ipyleaflet.TileLayerEE.__init__': ( 'ipyleaflet.html#tilelayeree.__init__',
40 | 'ee_jupyter/ipyleaflet.py')},
41 | 'ee_jupyter.layout': { 'ee_jupyter.layout.MapWithInspector': ('layout.html#mapwithinspector', 'ee_jupyter/layout.py'),
42 | 'ee_jupyter.layout.MapWithInspector.__init__': ( 'layout.html#mapwithinspector.__init__',
43 | 'ee_jupyter/layout.py')},
44 | 'ee_jupyter.rich_display': { 'ee_jupyter.rich_display.ee.Element.__repr__': ( 'rich_display.html#ee.element.__repr__',
45 | 'ee_jupyter/rich_display.py'),
46 | 'ee_jupyter.rich_display.ee.Element.__str__': ( 'rich_display.html#ee.element.__str__',
47 | 'ee_jupyter/rich_display.py'),
48 | 'ee_jupyter.rich_display.ee.Image.__add__': ( 'rich_display.html#ee.image.__add__',
49 | 'ee_jupyter/rich_display.py'),
50 | 'ee_jupyter.rich_display.ee.Image.__getitem__': ( 'rich_display.html#ee.image.__getitem__',
51 | 'ee_jupyter/rich_display.py'),
52 | 'ee_jupyter.rich_display.ee.Image.__mul__': ( 'rich_display.html#ee.image.__mul__',
53 | 'ee_jupyter/rich_display.py'),
54 | 'ee_jupyter.rich_display.ee.Image.__sub__': ( 'rich_display.html#ee.image.__sub__',
55 | 'ee_jupyter/rich_display.py'),
56 | 'ee_jupyter.rich_display.ee.Image.__truediv__': ( 'rich_display.html#ee.image.__truediv__',
57 | 'ee_jupyter/rich_display.py'),
58 | 'ee_jupyter.rich_display.ee.Image._ipython_display_': ( 'rich_display.html#ee.image._ipython_display_',
59 | 'ee_jupyter/rich_display.py')}}}
60 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/ee_jupyter/ipyleaflet.py:
--------------------------------------------------------------------------------
1 | # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_ipyleaflet.ipynb.
2 |
3 | # %% auto 0
4 | __all__ = ['logger', 'DEFAULT_MAP_HEIGHT', 'SCALE_LEVEL_0', 'TileLayerEE', 'Map', 'StructureTree', 'Inspector']
5 |
6 | # %% ../nbs/01_ipyleaflet.ipynb 4
7 | import ee
8 | import json
9 | import ipyleaflet
10 | import ipytree
11 | import ipywidgets as widgets
12 | import logging
13 | import traitlets
14 |
15 | # %% ../nbs/01_ipyleaflet.ipynb 5
16 | logger = logging.getLogger()
17 | logger.setLevel(logging.WARNING)
18 |
19 | # %% ../nbs/01_ipyleaflet.ipynb 7
20 | DEFAULT_MAP_HEIGHT = '400px'
21 |
22 | class TileLayerEE(ipyleaflet.TileLayer):
23 | """Class for a tile layer generated by Earth Engine.
24 |
25 | Attributes:
26 | ee_object: An Earth Engine object.
27 | """
28 | def __init__(self,
29 | ee_object:ee.Image, # An Earth Engine Image object
30 | *args,
31 | **kwargs):
32 |
33 | self.ee_object = ee_object
34 |
35 | super(TileLayerEE, self).__init__(*args, **kwargs)
36 |
37 |
38 | class Map(ipyleaflet.Map):
39 | """An interactive map class for Jupyter clients.
40 |
41 | Attributes:
42 | layers_control: a boolean indicating whether to display a layers control.
43 | """
44 |
45 | layers_control = traitlets.Bool(True)
46 |
47 | def __init__(self, *args, **kwargs):
48 |
49 | self.layers_control_instance = None
50 |
51 | # Set default values for the map.
52 | if 'zoom' not in kwargs:
53 | kwargs['zoom'] = 4
54 |
55 | if 'basemap' not in kwargs:
56 | kwargs['basemap'] = ipyleaflet.basemap_to_tiles(ipyleaflet.basemaps.Stamen.Watercolor)
57 |
58 | if 'height' not in kwargs:
59 | kwargs['height'] = DEFAULT_MAP_HEIGHT
60 |
61 | super(Map, self).__init__(*args, **kwargs)
62 |
63 | if self.layers_control:
64 | self.layers_control_instance = ipyleaflet.LayersControl(position='topright')
65 | self.add_control(self.layers_control_instance)
66 |
67 | self.default_style.cursor = 'crosshair'
68 |
69 | self.layout.width = '100%'
70 |
71 |
72 | def addLayer(self, eeObject, visParams={}, name=None, shown=True, opacity=1):
73 | """Adds a layer for an Earth Engine object."""
74 |
75 | if name is None:
76 | # Provide a default name for the layer in the form "Layer ##"
77 | name = f'Layer {len(self.layers)}'
78 |
79 | def get_tile_layer_url(ee_image_object):
80 | map_id_dict = ee.Image(ee_image_object).getMapId()
81 | return map_id_dict['tile_fetcher'].url_format
82 |
83 | # Assume that the eeObject is an ee.Image.
84 | # TODO: Generalize this to other EE objects.
85 | ee_image = eeObject
86 |
87 | tile_url = get_tile_layer_url(
88 | ee_image.visualize(**visParams)
89 | )
90 | self.add_layer(TileLayerEE(ee_object=eeObject, url=tile_url, name=name, visible=shown))
91 |
92 | # %% ../nbs/01_ipyleaflet.ipynb 15
93 | class StructureTree(ipytree.Tree):
94 |
95 | JSON_PREFIX = 'JSON: '
96 |
97 | def __init__(self, data, **kwargs):
98 | self.data = data
99 | super().__init__(StructureTree.__ipytreeify(data))
100 |
101 | @staticmethod
102 | def __ipytreeify(data) -> tuple:
103 | """Return a sequence of nodes"""
104 |
105 | def is_json(in_str):
106 | '''Determines if a string is JSON.'''
107 | return in_str.startswith(StructureTree.JSON_PREFIX)
108 |
109 | def inline_style(x, color='blue'):
110 | '''Wrap a string with inline HTML styling.'''
111 | return f'{x}'
112 |
113 | def handle_node_open(change):
114 | if change['new']:
115 | nodes_unpacked = []
116 | for node in change['owner'].nodes:
117 | # If there no subnodes, try to populate the subnodes.
118 | if len(node.nodes) == 0:
119 | if is_json(node.name):
120 | unpacked_json = json.loads(node.name[len(StructureTree.JSON_PREFIX):])
121 | if isinstance(unpacked_json, list):
122 | nodes_unpacked = StructureTree.__ipytreeify(unpacked_json)
123 | elif isinstance(unpacked_json, dict):
124 | nodes_unpacked = StructureTree.__ipytreeify(unpacked_json)
125 | else:
126 | raise
127 | else: # ~is_json(node.name)
128 | nodes_unpacked.append(node)
129 | change['owner'].nodes = nodes_unpacked
130 |
131 | if isinstance(data, list):
132 | node_list = []
133 | for count, el in enumerate(data):
134 | if isinstance(el, list):
135 | subnode = ipytree.Node(
136 | name=f'{inline_style("List")} ({len(el)} elements)',
137 | nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(el)}')],
138 | opened=False)
139 | subnode.observe(handle_node_open, names='opened')
140 | elif isinstance(el, dict):
141 | subnode = ipytree.Node(
142 | name=f'{inline_style("Object")} ({len(el)} elements)',
143 | nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(el)}')],
144 | opened=False)
145 | subnode.observe(handle_node_open, names='opened')
146 | else:
147 | subnode = ipytree.Node(f'{el}')
148 | node_list.append(subnode)
149 | return node_list
150 | elif isinstance(data, dict):
151 | node_list = []
152 | for key, value in data.items():
153 | if isinstance(value, list):
154 | subnode = ipytree.Node(
155 | name=f'{inline_style(key)}: List ({len(value)} elements)',
156 | nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(value)}')],
157 | opened=False)
158 | subnode.observe(handle_node_open, names='opened')
159 | elif isinstance(value, dict):
160 | subnode = ipytree.Node(
161 | name=f'{inline_style(key)}: Object ({len(value)} elements)',
162 | nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(value)}')],
163 | opened=False)
164 | subnode.observe(handle_node_open, names='opened')
165 | else:
166 | subnode = ipytree.Node(f'{inline_style(key)}: {value}')
167 | node_list.append(subnode)
168 | return node_list
169 | else:
170 | return (data, )
171 |
172 |
173 | # %% ../nbs/01_ipyleaflet.ipynb 19
174 | # Map scale at Level 0 in meters/pixel
175 | SCALE_LEVEL_0 = 156543.03392
176 |
177 | #class Inspector(ipytree.Tree):
178 | class Inspector(StructureTree):
179 | """Class representing an inspector tool that responds to map events."""
180 |
181 | def __init__(self,
182 | map_object=None, # An Earth Engine Image object
183 | *args,
184 | **kwargs):
185 |
186 | self.point_data = None
187 | # self.tree_data = None
188 | self.objects_data = {}
189 | self.pixels_data = {}
190 |
191 | self._disabled = False
192 |
193 | self.map_object = map_object
194 | self.layout.width = '100%'
195 | self.layout.max_height = '400px'
196 | self.layout.overflow = 'scroll'
197 |
198 | super(Inspector, self).__init__(
199 | data=['Click on the map to inspect the layers.'],
200 | *args,
201 | **kwargs
202 | )
203 |
204 | if map_object:
205 | self.set_map(map_object)
206 |
207 | # self.update_inspector()
208 |
209 | @property
210 | def point_node(self):
211 | return self.nodes[0]
212 |
213 | @point_node.setter
214 | def point_node(self, new_point_node):
215 | #(lat, lon) = new_coords
216 | _temp_nodes = list(self.nodes)
217 | _temp_nodes[0] = new_point_node
218 | self.nodes = _temp_nodes
219 |
220 | @property
221 | def pixels_node(self):
222 | return self.nodes[1]
223 |
224 | @property
225 | def objects_node(self):
226 | return self.nodes[2]
227 |
228 | @property
229 | def disabled(self):
230 | return self._disabled
231 |
232 | @disabled.setter
233 | def disabled(self, state):
234 | self._disabled = state
235 |
236 | def update_inspector(self, coords=None):
237 | """Update information in the inspector tree."""
238 |
239 | def _order_items(item_dict, ordering_list):
240 | """Orders dictionary items in a specified order."""
241 | list_of_tuples = [(key, item_dict[key]) for key in [x for x in ordering_list if x in item_dict.keys()]]
242 | return dict(list_of_tuples)
243 |
244 | if self.map_object:
245 | if not self.disabled and coords:
246 |
247 | self.nodes = [ipytree.Node('Loading...')]
248 |
249 | (lat, lon) = coords
250 |
251 | scale_approx = SCALE_LEVEL_0 / 2**self.map_object.zoom
252 | self.point_data = {
253 | 'Longitude': f'{lon:.6f}',
254 | 'Latitude': f'{lat:.6f}',
255 | 'Zoom Level': f'{self.map_object.zoom:.0f}',
256 | 'Scale (approx. m/px)': f'{scale_approx:.2f}'
257 | }
258 |
259 | # Update the Pixels folder
260 | for layer in self.map_object.layers:
261 | if not layer.base:
262 | ee_type = ee.Algorithms.ObjectType(layer.ee_object).getInfo()
263 |
264 | if ee_type == 'Image':
265 | value_dict = layer.ee_object.reduceRegion(
266 | reducer=ee.Reducer.mean(),
267 | geometry=ee.Geometry.Point(lon, lat),
268 | scale=scale_approx,
269 | bestEffort=True
270 | ).getInfo()
271 | num_bands = len(value_dict.keys())
272 |
273 | band_dict = {}
274 | has_unmasked_pixel = False
275 | for bandname in layer.ee_object.bandNames().getInfo():
276 | if value_dict[bandname] is not None:
277 | has_unmasked_pixel = True
278 | band_dict.update({f'{bandname}': f'{value_dict[bandname]:.4f}'})
279 | else:
280 | band_dict.update({f'{bandname}': f'masked'})
281 |
282 | # if not has_unmasked_pixel:
283 | # layer_node.nodes = [
284 | # ipytree.Node(f'No unmasked pixels at clicked point.'),
285 | # ]
286 | # pixel_nodes.append(layer_node)
287 | self.pixels_data.update({f'{layer.name}': band_dict})
288 |
289 | # Update the Objects folder
290 | for layer in self.map_object.layers:
291 | if not layer.base:
292 | ee_type = ee.Algorithms.ObjectType(layer.ee_object).getInfo()
293 | layer_info = layer.ee_object.getInfo()
294 |
295 | # Order the layer information.
296 | ordering_list = ['type', 'id', 'version', 'bands', 'properties']
297 | layer_info = _order_items(layer_info, ordering_list)
298 | layer_dict = {f'{layer.name}': layer_info}
299 |
300 | self.objects_data.update(layer_dict)
301 |
302 | self.nodes = [
303 | ipytree.Node('Point', nodes=StructureTree(self.point_data).nodes),
304 | ipytree.Node('Pixels', nodes=StructureTree(self.pixels_data).nodes, opened=False),
305 | ipytree.Node('Objects', nodes=StructureTree(self.objects_data).nodes, opened=False),
306 | ]
307 |
308 | def register_map(self, map_object):
309 | def handle_interaction(type, event, coordinates):
310 | if type == 'click':
311 | self.update_inspector(coordinates)
312 | map_object.on_interaction(handle_interaction)
313 |
314 | def set_map(self, map_object):
315 | self.map_object = map_object
316 | self.register_map(map_object)
317 |
318 | def get_map(self):
319 | return self.map_object
320 |
--------------------------------------------------------------------------------
/nbs/02_layout.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "id": "94288810-bd72-4341-8a86-96ae2ae5848d",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "#|hide\n",
11 | "# Copyright 2022 Google LLC\n",
12 | "#\n",
13 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
14 | "# you may not use this file except in compliance with the License.\n",
15 | "# You may obtain a copy of the License at\n",
16 | "# \n",
17 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
18 | "# \n",
19 | "# Unless required by applicable law or agreed to in writing, software\n",
20 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
21 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
22 | "# See the License for the specific language governing permissions and\n",
23 | "# limitations under the License."
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "id": "5bde49e7-42e2-4483-a14f-3f7ec7f32fc1",
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "#| default_exp layout"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "7ace02d3-fb7d-4128-ba94-5c2a883b0fd2",
39 | "metadata": {},
40 | "source": [
41 | "# layout module\n",
42 | "\n",
43 | "> Provides panels of common layouts of map & inspector widgets."
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "id": "b9f705ad-d869-413f-8eb6-3890a47d6440",
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "#|hide\n",
54 | "from nbdev.showdoc import *"
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "id": "d5d1a7e1-46f9-4bc4-adbb-0db383d453b1",
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "#|export\n",
65 | "from ee_jupyter.ipyleaflet import Map\n",
66 | "from ee_jupyter.ipyleaflet import Inspector\n",
67 | "import ee\n",
68 | "import ipyleaflet\n",
69 | "import ipywidgets as widgets\n",
70 | "import logging"
71 | ]
72 | },
73 | {
74 | "cell_type": "code",
75 | "execution_count": null,
76 | "id": "b45a5144-7064-4e79-b8b2-8380a1424266",
77 | "metadata": {},
78 | "outputs": [],
79 | "source": [
80 | "logger = logging.getLogger()\n",
81 | "logger.setLevel(logging.WARNING)"
82 | ]
83 | },
84 | {
85 | "cell_type": "markdown",
86 | "id": "bbd4e260-f0a7-49e9-b61f-faeb6647a4f9",
87 | "metadata": {},
88 | "source": [
89 | "## Inspector layouts"
90 | ]
91 | },
92 | {
93 | "cell_type": "markdown",
94 | "id": "e69e6c11-318d-4375-b4cd-e91a787cae6f",
95 | "metadata": {},
96 | "source": [
97 | "### Map with Inspector"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "id": "da6d0c44-3929-4c6f-826c-ab73fe72efa9",
104 | "metadata": {},
105 | "outputs": [],
106 | "source": [
107 | "#|export\n",
108 | "class MapWithInspector(widgets.HBox):\n",
109 | " \"\"\"A map with a tobbleable inspector.\"\"\"\n",
110 | " \n",
111 | " # Font Awesome icons: https://fontawesome.com/v4/examples/\n",
112 | " ICON_INSPECTOR_SHOWN = 'info-circle 2x fw inverse'\n",
113 | " ICON_INSPECTOR_HIDDEN = 'info-circle 2x fw'\n",
114 | " \n",
115 | " def __init__(self, **kwargs):\n",
116 | " \n",
117 | " self.map = Map(**kwargs)\n",
118 | " \n",
119 | " button_inspector_toggle = widgets.Button(\n",
120 | " icon = self.ICON_INSPECTOR_SHOWN,\n",
121 | " layout = widgets.Layout(width='45px', height='45px', padding='0px')\n",
122 | " )\n",
123 | " \n",
124 | " def show_inspector(visible):\n",
125 | " if visible:\n",
126 | " self.inspector.layout.width = None\n",
127 | " self.inspector.layout.min_width = f'300px'\n",
128 | " self.inspector.disabled = False\n",
129 | " else:\n",
130 | " self.inspector.layout.width = f'0%'\n",
131 | " self.inspector.layout.min_width = None\n",
132 | " self.inspector.disabled = True\n",
133 | " \n",
134 | " def on_button_clicked(_b):\n",
135 | " if _b.icon == self.ICON_INSPECTOR_HIDDEN:\n",
136 | " show_inspector(False)\n",
137 | " _b.icon = self.ICON_INSPECTOR_SHOWN\n",
138 | " else:\n",
139 | " show_inspector(True)\n",
140 | " _b.icon = self.ICON_INSPECTOR_HIDDEN\n",
141 | " button_inspector_toggle.on_click(on_button_clicked)\n",
142 | " \n",
143 | " widget_control1 = ipyleaflet.WidgetControl(widget=button_inspector_toggle,\n",
144 | " position='topright')\n",
145 | " self.map.add_control(widget_control1)\n",
146 | " self.inspector = Inspector(self.map)\n",
147 | " \n",
148 | " kwargs['children'] = [self.map, self.inspector]\n",
149 | " show_inspector(False)\n",
150 | " \n",
151 | " super().__init__(**kwargs)"
152 | ]
153 | },
154 | {
155 | "cell_type": "code",
156 | "execution_count": null,
157 | "id": "eb92e252-d2dd-41ca-ac63-f5a9cb38a964",
158 | "metadata": {},
159 | "outputs": [],
160 | "source": [
161 | "ee.Initialize()\n",
162 | "\n",
163 | "mt = MapWithInspector(center=(37.5924, -122.09), zoom=8)\n",
164 | "mt.map.addLayer(ee.Image.pixelLonLat(), {'min':-90, 'max':90, 'opacity':0.5}, 'LonLat')\n",
165 | "mt.map.addLayer(\n",
166 | " ee.Image('LANDSAT/LC09/C02/T1_L2/LC09_187058_20220105'),\n",
167 | " {'min':0, 'max':90, 'opacity':0.5},\n",
168 | " 'Landsat')\n",
169 | "mt"
170 | ]
171 | },
172 | {
173 | "cell_type": "code",
174 | "execution_count": null,
175 | "id": "8408c133-dced-405b-8f7f-f64fbfd9642b",
176 | "metadata": {},
177 | "outputs": [],
178 | "source": [
179 | "#| hide\n",
180 | "import nbdev; nbdev.nbdev_export()"
181 | ]
182 | }
183 | ],
184 | "metadata": {
185 | "kernelspec": {
186 | "display_name": "Python 3 (ipykernel)",
187 | "language": "python",
188 | "name": "python3"
189 | },
190 | "widgets": {
191 | "application/vnd.jupyter.widget-state+json": {
192 | "state": {
193 | "0348e638d952476886c1b67898ce2efa": {
194 | "model_module": "@jupyter-widgets/controls",
195 | "model_module_version": "2.0.0",
196 | "model_name": "HBoxModel",
197 | "state": {
198 | "children": [
199 | "IPY_MODEL_4475643b95a645ef9dd0394099021742",
200 | "IPY_MODEL_060e7aa9d80a41ef96c4f3e1c4784cdb"
201 | ],
202 | "layout": "IPY_MODEL_a8a8fbbb1a344d06bfc8bfb3f5071260"
203 | }
204 | },
205 | "060e7aa9d80a41ef96c4f3e1c4784cdb": {
206 | "model_module": "ipytree",
207 | "model_module_version": "^0.2",
208 | "model_name": "TreeModel",
209 | "state": {
210 | "_model_module_version": "^0.2",
211 | "_view_module_version": "^0.2",
212 | "layout": "IPY_MODEL_ca0daae1124d4046ad85c02468a5d4cd",
213 | "nodes": [
214 | "IPY_MODEL_205b1e193efa4e7aa16aa7557f1d3e84"
215 | ]
216 | }
217 | },
218 | "09abd7603e1947ad9ea2e33c3a052f79": {
219 | "model_module": "@jupyter-widgets/controls",
220 | "model_module_version": "2.0.0",
221 | "model_name": "ButtonModel",
222 | "state": {
223 | "icon": "info-circle 2x fw inverse",
224 | "layout": "IPY_MODEL_369ad0d1a60b4823ae9d7b7bd8a3fae0",
225 | "style": "IPY_MODEL_a06a49b1f62b4560a16551db1afb383d",
226 | "tooltip": null
227 | }
228 | },
229 | "121ce27b46384c9e9cdd8690ee4f8233": {
230 | "model_module": "jupyter-leaflet",
231 | "model_module_version": "^0.17",
232 | "model_name": "LeafletTileLayerModel",
233 | "state": {
234 | "_model_module_version": "^0.17",
235 | "_view_module_version": "^0.17",
236 | "name": "LonLat",
237 | "options": [
238 | "attribution",
239 | "bounds",
240 | "detect_retina",
241 | "max_native_zoom",
242 | "max_zoom",
243 | "min_native_zoom",
244 | "min_zoom",
245 | "no_wrap",
246 | "tile_size",
247 | "tms",
248 | "zoom_offset"
249 | ],
250 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/94684f9069ec559fbd6d13a6e0b6ee1c-47daebef121db141d808c72d796843fa/tiles/{z}/{x}/{y}"
251 | }
252 | },
253 | "1aa0513de1d240d7ac0ed5276913584c": {
254 | "model_module": "jupyter-leaflet",
255 | "model_module_version": "^0.17",
256 | "model_name": "LeafletTileLayerModel",
257 | "state": {
258 | "_model_module_version": "^0.17",
259 | "_view_module_version": "^0.17",
260 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
261 | "base": true,
262 | "max_zoom": 16,
263 | "min_zoom": 1,
264 | "name": "Stamen.Watercolor",
265 | "options": [
266 | "attribution",
267 | "bounds",
268 | "detect_retina",
269 | "max_native_zoom",
270 | "max_zoom",
271 | "min_native_zoom",
272 | "min_zoom",
273 | "no_wrap",
274 | "tile_size",
275 | "tms",
276 | "zoom_offset"
277 | ],
278 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
279 | }
280 | },
281 | "1d339c7b95cb4b6eb2d891acb9a530bd": {
282 | "model_module": "jupyter-leaflet",
283 | "model_module_version": "^0.17",
284 | "model_name": "LeafletZoomControlModel",
285 | "state": {
286 | "_model_module_version": "^0.17",
287 | "_view_module_version": "^0.17",
288 | "options": [
289 | "position",
290 | "zoom_in_text",
291 | "zoom_in_title",
292 | "zoom_out_text",
293 | "zoom_out_title"
294 | ]
295 | }
296 | },
297 | "205b1e193efa4e7aa16aa7557f1d3e84": {
298 | "model_module": "ipytree",
299 | "model_module_version": "^0.2",
300 | "model_name": "NodeModel",
301 | "state": {
302 | "_id": "c3987ec6-b01e-4ea4-91cc-fe0916058119",
303 | "_model_module_version": "^0.2",
304 | "_view_module_version": "^0.2",
305 | "name": "Click on the map to inspect the layers."
306 | }
307 | },
308 | "2ddbf75121c14d5bb7a9e5c846862ed4": {
309 | "model_module": "jupyter-leaflet",
310 | "model_module_version": "^0.17",
311 | "model_name": "LeafletMapStyleModel",
312 | "state": {
313 | "_model_module_version": "^0.17"
314 | }
315 | },
316 | "369ad0d1a60b4823ae9d7b7bd8a3fae0": {
317 | "model_module": "@jupyter-widgets/base",
318 | "model_module_version": "2.0.0",
319 | "model_name": "LayoutModel",
320 | "state": {
321 | "height": "45px",
322 | "padding": "0px",
323 | "width": "45px"
324 | }
325 | },
326 | "4475643b95a645ef9dd0394099021742": {
327 | "model_module": "jupyter-leaflet",
328 | "model_module_version": "^0.17",
329 | "model_name": "LeafletMapModel",
330 | "state": {
331 | "_model_module_version": "^0.17",
332 | "_view_module_version": "^0.17",
333 | "bottom": 25573,
334 | "center": [
335 | 37.5924,
336 | -122.09
337 | ],
338 | "controls": [
339 | "IPY_MODEL_1d339c7b95cb4b6eb2d891acb9a530bd",
340 | "IPY_MODEL_a805777a1c7a420eb215fcc95e05f946",
341 | "IPY_MODEL_8cc930b450344fbbb377c4b48070e8b2",
342 | "IPY_MODEL_46a8df608a1f410aaeaaed4792610361"
343 | ],
344 | "default_style": "IPY_MODEL_51d11b01ff444bbba4da6337a723ae83",
345 | "dragging_style": "IPY_MODEL_d8c9de1b0ffb404aba9f15f8ece44906",
346 | "east": -119.65759277343751,
347 | "fullscreen": false,
348 | "interpolation": "bilinear",
349 | "layers": [
350 | "IPY_MODEL_1aa0513de1d240d7ac0ed5276913584c",
351 | "IPY_MODEL_121ce27b46384c9e9cdd8690ee4f8233",
352 | "IPY_MODEL_6b453cf26a674bd0816ffaa1b4abce61"
353 | ],
354 | "layout": "IPY_MODEL_50b2449c808e448196c573a1f49050da",
355 | "left": 10099,
356 | "modisdate": "2023-01-10",
357 | "north": 38.45789034424927,
358 | "options": [
359 | "bounce_at_zoom_limits",
360 | "box_zoom",
361 | "center",
362 | "close_popup_on_click",
363 | "double_click_zoom",
364 | "dragging",
365 | "fullscreen",
366 | "inertia",
367 | "inertia_deceleration",
368 | "inertia_max_speed",
369 | "interpolation",
370 | "keyboard",
371 | "keyboard_pan_offset",
372 | "keyboard_zoom_offset",
373 | "max_zoom",
374 | "min_zoom",
375 | "prefer_canvas",
376 | "scroll_wheel_zoom",
377 | "tap",
378 | "tap_tolerance",
379 | "touch_zoom",
380 | "world_copy_jump",
381 | "zoom",
382 | "zoom_animation_threshold",
383 | "zoom_delta",
384 | "zoom_snap"
385 | ],
386 | "prefer_canvas": false,
387 | "right": 10985,
388 | "south": 36.71687068791304,
389 | "style": "IPY_MODEL_2ddbf75121c14d5bb7a9e5c846862ed4",
390 | "top": 25173,
391 | "west": -124.52453613281251,
392 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/02_layout.ipynb",
393 | "zoom": 8
394 | }
395 | },
396 | "46a8df608a1f410aaeaaed4792610361": {
397 | "model_module": "jupyter-leaflet",
398 | "model_module_version": "^0.17",
399 | "model_name": "LeafletWidgetControlModel",
400 | "state": {
401 | "_model_module": "jupyter-leaflet",
402 | "_model_module_version": "^0.17",
403 | "_view_count": null,
404 | "_view_module": "jupyter-leaflet",
405 | "_view_module_version": "^0.17",
406 | "options": [
407 | "position",
408 | "transparent_bg"
409 | ],
410 | "position": "topright",
411 | "widget": "IPY_MODEL_09abd7603e1947ad9ea2e33c3a052f79"
412 | }
413 | },
414 | "50b2449c808e448196c573a1f49050da": {
415 | "model_module": "@jupyter-widgets/base",
416 | "model_module_version": "2.0.0",
417 | "model_name": "LayoutModel",
418 | "state": {
419 | "width": "100%"
420 | }
421 | },
422 | "51d11b01ff444bbba4da6337a723ae83": {
423 | "model_module": "jupyter-leaflet",
424 | "model_module_version": "^0.17",
425 | "model_name": "LeafletMapStyleModel",
426 | "state": {
427 | "_model_module_version": "^0.17",
428 | "cursor": "crosshair"
429 | }
430 | },
431 | "6b453cf26a674bd0816ffaa1b4abce61": {
432 | "model_module": "jupyter-leaflet",
433 | "model_module_version": "^0.17",
434 | "model_name": "LeafletTileLayerModel",
435 | "state": {
436 | "_model_module_version": "^0.17",
437 | "_view_module_version": "^0.17",
438 | "name": "Landsat",
439 | "options": [
440 | "attribution",
441 | "bounds",
442 | "detect_retina",
443 | "max_native_zoom",
444 | "max_zoom",
445 | "min_native_zoom",
446 | "min_zoom",
447 | "no_wrap",
448 | "tile_size",
449 | "tms",
450 | "zoom_offset"
451 | ],
452 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/6ad5740ea0606cba020bc83b7def6383-d6df261b4d34a08af9f991ac81f4f5c2/tiles/{z}/{x}/{y}"
453 | }
454 | },
455 | "8cc930b450344fbbb377c4b48070e8b2": {
456 | "model_module": "jupyter-leaflet",
457 | "model_module_version": "^0.17",
458 | "model_name": "LeafletLayersControlModel",
459 | "state": {
460 | "_model_module_version": "^0.17",
461 | "_view_module_version": "^0.17",
462 | "options": [
463 | "position"
464 | ],
465 | "position": "topright"
466 | }
467 | },
468 | "a06a49b1f62b4560a16551db1afb383d": {
469 | "model_module": "@jupyter-widgets/controls",
470 | "model_module_version": "2.0.0",
471 | "model_name": "ButtonStyleModel",
472 | "state": {
473 | "font_family": null,
474 | "font_size": null,
475 | "font_style": null,
476 | "font_variant": null,
477 | "font_weight": null,
478 | "text_color": null,
479 | "text_decoration": null
480 | }
481 | },
482 | "a805777a1c7a420eb215fcc95e05f946": {
483 | "model_module": "jupyter-leaflet",
484 | "model_module_version": "^0.17",
485 | "model_name": "LeafletAttributionControlModel",
486 | "state": {
487 | "_model_module_version": "^0.17",
488 | "_view_module_version": "^0.17",
489 | "options": [
490 | "position",
491 | "prefix"
492 | ],
493 | "position": "bottomright",
494 | "prefix": "ipyleaflet"
495 | }
496 | },
497 | "a8a8fbbb1a344d06bfc8bfb3f5071260": {
498 | "model_module": "@jupyter-widgets/base",
499 | "model_module_version": "2.0.0",
500 | "model_name": "LayoutModel",
501 | "state": {}
502 | },
503 | "ca0daae1124d4046ad85c02468a5d4cd": {
504 | "model_module": "@jupyter-widgets/base",
505 | "model_module_version": "2.0.0",
506 | "model_name": "LayoutModel",
507 | "state": {
508 | "max_height": "400px",
509 | "overflow": "scroll",
510 | "width": "0%"
511 | }
512 | },
513 | "d8c9de1b0ffb404aba9f15f8ece44906": {
514 | "model_module": "jupyter-leaflet",
515 | "model_module_version": "^0.17",
516 | "model_name": "LeafletMapStyleModel",
517 | "state": {
518 | "_model_module_version": "^0.17",
519 | "cursor": "move"
520 | }
521 | }
522 | },
523 | "version_major": 2,
524 | "version_minor": 0
525 | }
526 | }
527 | },
528 | "nbformat": 4,
529 | "nbformat_minor": 5
530 | }
531 |
--------------------------------------------------------------------------------
/nbs/index.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "raw",
5 | "metadata": {},
6 | "source": [
7 | "---\n",
8 | "execute:\n",
9 | " enabled: true\n",
10 | "---"
11 | ]
12 | },
13 | {
14 | "cell_type": "code",
15 | "execution_count": null,
16 | "metadata": {},
17 | "outputs": [],
18 | "source": [
19 | "#|hide\n",
20 | "# Copyright 2022 Google LLC\n",
21 | "#\n",
22 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
23 | "# you may not use this file except in compliance with the License.\n",
24 | "# You may obtain a copy of the License at\n",
25 | "# \n",
26 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
27 | "# \n",
28 | "# Unless required by applicable law or agreed to in writing, software\n",
29 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
30 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
31 | "# See the License for the specific language governing permissions and\n",
32 | "# limitations under the License."
33 | ]
34 | },
35 | {
36 | "cell_type": "markdown",
37 | "metadata": {},
38 | "source": [
39 | "# earthengine-jupyter\n",
40 | "\n",
41 | "> Tools for working with the Earth Engine from a Jupyter development environment"
42 | ]
43 | },
44 | {
45 | "cell_type": "markdown",
46 | "metadata": {},
47 | "source": [
48 | "**NOTICE: This is an experimental project and is not an officially supported Google project. You are welcome to use it, but we do not guarantee stability.**"
49 | ]
50 | },
51 | {
52 | "cell_type": "markdown",
53 | "metadata": {},
54 | "source": [
55 | "## Setup"
56 | ]
57 | },
58 | {
59 | "cell_type": "code",
60 | "execution_count": null,
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "try:\n",
65 | " import ee_jupyter\n",
66 | " print('ee_jupyter was already installed.')\n",
67 | "except ModuleNotFoundError:\n",
68 | " print('ee_jupyter was not found. Installing now...')\n",
69 | " import os\n",
70 | " result = os.system('pip -q install earthengine-jupyter')"
71 | ]
72 | },
73 | {
74 | "cell_type": "markdown",
75 | "metadata": {},
76 | "source": [
77 | "## How to use"
78 | ]
79 | },
80 | {
81 | "cell_type": "markdown",
82 | "metadata": {},
83 | "source": [
84 | "This lib contains a `Map` class that can be used to display an interactive map."
85 | ]
86 | },
87 | {
88 | "cell_type": "code",
89 | "execution_count": null,
90 | "metadata": {},
91 | "outputs": [],
92 | "source": [
93 | "import ee\n",
94 | "from ee_jupyter.core import authenticate_if_needed\n",
95 | "from ee_jupyter.ipyleaflet import Map\n",
96 | "from ee_jupyter.layout import MapWithInspector\n",
97 | "import ipywidgets as widgets"
98 | ]
99 | },
100 | {
101 | "cell_type": "code",
102 | "execution_count": null,
103 | "metadata": {},
104 | "outputs": [],
105 | "source": [
106 | "authenticate_if_needed()"
107 | ]
108 | },
109 | {
110 | "cell_type": "code",
111 | "execution_count": null,
112 | "metadata": {},
113 | "outputs": [],
114 | "source": [
115 | "# Intialize the Earth Engine client library.\n",
116 | "ee.Initialize()"
117 | ]
118 | },
119 | {
120 | "cell_type": "code",
121 | "execution_count": null,
122 | "metadata": {},
123 | "outputs": [],
124 | "source": [
125 | "map1 = Map(center=(37.5924, -122.09), zoom=8)\n",
126 | "map1"
127 | ]
128 | },
129 | {
130 | "cell_type": "markdown",
131 | "metadata": {},
132 | "source": [
133 | "Define an Earth Engine image layer, and add it to the interactive map."
134 | ]
135 | },
136 | {
137 | "cell_type": "code",
138 | "execution_count": null,
139 | "metadata": {},
140 | "outputs": [],
141 | "source": [
142 | "img1 = ee.Image(\"LANDSAT/LC09/C02/T1_L2/LC09_044034_20220127\")\n",
143 | "visualization = {\n",
144 | " 'bands': ['SR_B4', 'SR_B3', 'SR_B2'],\n",
145 | " 'min': 0.2 / 0.0000275,\n",
146 | " 'max': 0.4 / 0.0000275,\n",
147 | "}\n",
148 | "map1.addLayer(eeObject=img1, visParams=visualization, name='Landsat scene')"
149 | ]
150 | },
151 | {
152 | "cell_type": "markdown",
153 | "metadata": {},
154 | "source": [
155 | "We can also create an inspector object and associate it with the previously created map."
156 | ]
157 | },
158 | {
159 | "cell_type": "code",
160 | "execution_count": null,
161 | "metadata": {},
162 | "outputs": [],
163 | "source": [
164 | "from ee_jupyter.ipyleaflet import Inspector\n",
165 | "\n",
166 | "inspector1 = Inspector(map_object=map1)\n",
167 | "inspector1"
168 | ]
169 | },
170 | {
171 | "cell_type": "markdown",
172 | "metadata": {},
173 | "source": [
174 | "Typically when you create a inspector object, you will want to display it with the map. The `MapWithInpsector` object adds a button that toggles the inspector functionality."
175 | ]
176 | },
177 | {
178 | "cell_type": "markdown",
179 | "metadata": {},
180 | "source": [
181 | "The map below shows a Sentinel-2 image covering Paris. Click on the inspector toggle button to open the inspector."
182 | ]
183 | },
184 | {
185 | "cell_type": "code",
186 | "execution_count": null,
187 | "metadata": {},
188 | "outputs": [],
189 | "source": [
190 | "map_init_paris = {'center':(49.4, 2.3), 'zoom':8}\n",
191 | "m = MapWithInspector(**map_init_paris)\n",
192 | "image = ee.Image('COPERNICUS/S2_SR_HARMONIZED/20220604T104619_20220604T104620_T31UDQ')\n",
193 | "m.map.addLayer(image, {'bands': ['B4', 'B3', 'B2'], 'max': 2500}, 'Landsat image')\n",
194 | "m"
195 | ]
196 | },
197 | {
198 | "cell_type": "markdown",
199 | "metadata": {},
200 | "source": [
201 | ":::{.callout-tip}\n",
202 | "## Tip With Caption\n",
203 | "\n",
204 | "Note that when viewed on GitHub Pages you can manipulate Jupyter widgets independently, but the widgets do not interact with each other. To experience the cross-widget interactivity, open up this notebook in a Jupyter environment. \n",
205 | ":::"
206 | ]
207 | },
208 | {
209 | "cell_type": "markdown",
210 | "metadata": {},
211 | "source": [
212 | "# Displaying a Map Image"
213 | ]
214 | },
215 | {
216 | "cell_type": "markdown",
217 | "metadata": {},
218 | "source": [
219 | "If you want to display a static (non-interactive) image, you can do that as well. The `embed=True` parameter will allow the image to be saved within the notebook."
220 | ]
221 | },
222 | {
223 | "cell_type": "code",
224 | "execution_count": null,
225 | "metadata": {},
226 | "outputs": [],
227 | "source": [
228 | "from IPython.display import Image\n",
229 | "\n",
230 | "\n",
231 | "visualization['dimensions'] = 400 # maximum dimension for the image\n",
232 | "url = img1.getThumbUrl(visualization)\n",
233 | "\n",
234 | "Image(url=url, format='png', embed=True)"
235 | ]
236 | }
237 | ],
238 | "metadata": {
239 | "kernelspec": {
240 | "display_name": "Python 3 (ipykernel)",
241 | "language": "python",
242 | "name": "python3"
243 | },
244 | "widgets": {
245 | "application/vnd.jupyter.widget-state+json": {
246 | "state": {
247 | "18534294b7404458989fce691d6dd928": {
248 | "model_module": "jupyter-leaflet",
249 | "model_module_version": "^0.17",
250 | "model_name": "LeafletAttributionControlModel",
251 | "state": {
252 | "_model_module_version": "^0.17",
253 | "_view_module_version": "^0.17",
254 | "options": [
255 | "position",
256 | "prefix"
257 | ],
258 | "position": "bottomright",
259 | "prefix": "ipyleaflet"
260 | }
261 | },
262 | "1e1aec83e42045bf9ccff2e7ea1f3552": {
263 | "model_module": "jupyter-leaflet",
264 | "model_module_version": "^0.17",
265 | "model_name": "LeafletWidgetControlModel",
266 | "state": {
267 | "_model_module": "jupyter-leaflet",
268 | "_model_module_version": "^0.17",
269 | "_view_count": null,
270 | "_view_module": "jupyter-leaflet",
271 | "_view_module_version": "^0.17",
272 | "options": [
273 | "position",
274 | "transparent_bg"
275 | ],
276 | "position": "topright",
277 | "widget": "IPY_MODEL_e63129a6b6214b86b55161f9b3b01707"
278 | }
279 | },
280 | "2319a7c35eb146fab3231d5b3c2d223f": {
281 | "model_module": "ipytree",
282 | "model_module_version": "^0.2",
283 | "model_name": "NodeModel",
284 | "state": {
285 | "_id": "6615fdb1-b4b4-4271-bdd5-1a3df55b6834",
286 | "_model_module_version": "^0.2",
287 | "_view_module_version": "^0.2",
288 | "name": "Click on the map to inspect the layers."
289 | }
290 | },
291 | "2625695324c1472cbab709507ae2db40": {
292 | "model_module": "jupyter-leaflet",
293 | "model_module_version": "^0.17",
294 | "model_name": "LeafletZoomControlModel",
295 | "state": {
296 | "_model_module_version": "^0.17",
297 | "_view_module_version": "^0.17",
298 | "options": [
299 | "position",
300 | "zoom_in_text",
301 | "zoom_in_title",
302 | "zoom_out_text",
303 | "zoom_out_title"
304 | ]
305 | }
306 | },
307 | "29efe12ec397463da27a60cd3b558f3f": {
308 | "model_module": "jupyter-leaflet",
309 | "model_module_version": "^0.17",
310 | "model_name": "LeafletMapStyleModel",
311 | "state": {
312 | "_model_module_version": "^0.17",
313 | "cursor": "crosshair"
314 | }
315 | },
316 | "2da9251158dd4165bdd4dbef7cef2d5b": {
317 | "model_module": "ipytree",
318 | "model_module_version": "^0.2",
319 | "model_name": "NodeModel",
320 | "state": {
321 | "_id": "e6535316-8fcc-48fa-99f7-402223e43e54",
322 | "_model_module_version": "^0.2",
323 | "_view_module_version": "^0.2",
324 | "name": "Click on the map to inspect the layers."
325 | }
326 | },
327 | "336ea372a26f40318a5efb5d3594ffbd": {
328 | "model_module": "@jupyter-widgets/base",
329 | "model_module_version": "2.0.0",
330 | "model_name": "LayoutModel",
331 | "state": {
332 | "width": "100%"
333 | }
334 | },
335 | "3c36a4f289d04fb2886e978be5b06a17": {
336 | "model_module": "@jupyter-widgets/base",
337 | "model_module_version": "2.0.0",
338 | "model_name": "LayoutModel",
339 | "state": {
340 | "width": "100%"
341 | }
342 | },
343 | "47bb304079cd400bbe098184343ad277": {
344 | "model_module": "@jupyter-widgets/base",
345 | "model_module_version": "2.0.0",
346 | "model_name": "LayoutModel",
347 | "state": {
348 | "max_height": "400px",
349 | "overflow": "scroll",
350 | "width": "0%"
351 | }
352 | },
353 | "4f8b472c81eb412f9f90be778d5b046f": {
354 | "model_module": "@jupyter-widgets/base",
355 | "model_module_version": "2.0.0",
356 | "model_name": "LayoutModel",
357 | "state": {
358 | "max_height": "400px",
359 | "overflow": "scroll",
360 | "width": "100%"
361 | }
362 | },
363 | "505a42fde6424bdfb971ae73d8a26c74": {
364 | "model_module": "@jupyter-widgets/base",
365 | "model_module_version": "2.0.0",
366 | "model_name": "LayoutModel",
367 | "state": {
368 | "height": "45px",
369 | "padding": "0px",
370 | "width": "45px"
371 | }
372 | },
373 | "51d3cd5c24bb40f48c1508c09a932f11": {
374 | "model_module": "jupyter-leaflet",
375 | "model_module_version": "^0.17",
376 | "model_name": "LeafletTileLayerModel",
377 | "state": {
378 | "_model_module_version": "^0.17",
379 | "_view_module_version": "^0.17",
380 | "name": "Landsat image",
381 | "options": [
382 | "attribution",
383 | "bounds",
384 | "detect_retina",
385 | "max_native_zoom",
386 | "max_zoom",
387 | "min_native_zoom",
388 | "min_zoom",
389 | "no_wrap",
390 | "tile_size",
391 | "tms",
392 | "zoom_offset"
393 | ],
394 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/4c51a3d4f17a77f561c5d23cff888f0b-53fbcb0a51533daa78bd3a67b97d62d9/tiles/{z}/{x}/{y}"
395 | }
396 | },
397 | "5d27cf14d73a41aeab2ea720a257ca70": {
398 | "model_module": "jupyter-leaflet",
399 | "model_module_version": "^0.17",
400 | "model_name": "LeafletMapStyleModel",
401 | "state": {
402 | "_model_module_version": "^0.17",
403 | "cursor": "move"
404 | }
405 | },
406 | "6339a07fac08447aa064cf7417a55f6e": {
407 | "model_module": "jupyter-leaflet",
408 | "model_module_version": "^0.17",
409 | "model_name": "LeafletMapStyleModel",
410 | "state": {
411 | "_model_module_version": "^0.17",
412 | "cursor": "crosshair"
413 | }
414 | },
415 | "682f82c511af4356a71ab51f4288b6e9": {
416 | "model_module": "jupyter-leaflet",
417 | "model_module_version": "^0.17",
418 | "model_name": "LeafletMapStyleModel",
419 | "state": {
420 | "_model_module_version": "^0.17",
421 | "cursor": "move"
422 | }
423 | },
424 | "6e2f1f92660c40d3b3dd67547e65f278": {
425 | "model_module": "ipytree",
426 | "model_module_version": "^0.2",
427 | "model_name": "TreeModel",
428 | "state": {
429 | "_model_module_version": "^0.2",
430 | "_view_module_version": "^0.2",
431 | "layout": "IPY_MODEL_47bb304079cd400bbe098184343ad277",
432 | "nodes": [
433 | "IPY_MODEL_2da9251158dd4165bdd4dbef7cef2d5b"
434 | ]
435 | }
436 | },
437 | "75f3da4f32104f3b99e6f0886fdd01fb": {
438 | "model_module": "@jupyter-widgets/controls",
439 | "model_module_version": "2.0.0",
440 | "model_name": "HBoxModel",
441 | "state": {
442 | "children": [
443 | "IPY_MODEL_b305b8311ddd4319920041f8c51cdc69",
444 | "IPY_MODEL_6e2f1f92660c40d3b3dd67547e65f278"
445 | ],
446 | "layout": "IPY_MODEL_7aef74e0d48b416eaab11172e171c3ee"
447 | }
448 | },
449 | "775711c799aa4a1e83a8871807e4e87a": {
450 | "model_module": "jupyter-leaflet",
451 | "model_module_version": "^0.17",
452 | "model_name": "LeafletTileLayerModel",
453 | "state": {
454 | "_model_module_version": "^0.17",
455 | "_view_module_version": "^0.17",
456 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
457 | "base": true,
458 | "max_zoom": 16,
459 | "min_zoom": 1,
460 | "name": "Stamen.Watercolor",
461 | "options": [
462 | "attribution",
463 | "bounds",
464 | "detect_retina",
465 | "max_native_zoom",
466 | "max_zoom",
467 | "min_native_zoom",
468 | "min_zoom",
469 | "no_wrap",
470 | "tile_size",
471 | "tms",
472 | "zoom_offset"
473 | ],
474 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
475 | }
476 | },
477 | "7aef74e0d48b416eaab11172e171c3ee": {
478 | "model_module": "@jupyter-widgets/base",
479 | "model_module_version": "2.0.0",
480 | "model_name": "LayoutModel",
481 | "state": {}
482 | },
483 | "8e5a271e635948deb4e3362a6b1b91b2": {
484 | "model_module": "jupyter-leaflet",
485 | "model_module_version": "^0.17",
486 | "model_name": "LeafletLayersControlModel",
487 | "state": {
488 | "_model_module_version": "^0.17",
489 | "_view_module_version": "^0.17",
490 | "options": [
491 | "position"
492 | ],
493 | "position": "topright"
494 | }
495 | },
496 | "99287d20cfba47949e23c59052a876f5": {
497 | "model_module": "jupyter-leaflet",
498 | "model_module_version": "^0.17",
499 | "model_name": "LeafletMapModel",
500 | "state": {
501 | "_model_module_version": "^0.17",
502 | "_view_module_version": "^0.17",
503 | "bottom": 25573,
504 | "center": [
505 | 37.5924,
506 | -122.09
507 | ],
508 | "controls": [
509 | "IPY_MODEL_9abf5ae099f048f5beaeeba9754b737e",
510 | "IPY_MODEL_18534294b7404458989fce691d6dd928",
511 | "IPY_MODEL_c2ab078daf744726a06e51b73d99e2ee"
512 | ],
513 | "default_style": "IPY_MODEL_6339a07fac08447aa064cf7417a55f6e",
514 | "dragging_style": "IPY_MODEL_682f82c511af4356a71ab51f4288b6e9",
515 | "east": -119.64660644531251,
516 | "fullscreen": false,
517 | "interpolation": "bilinear",
518 | "layers": [
519 | "IPY_MODEL_775711c799aa4a1e83a8871807e4e87a",
520 | "IPY_MODEL_b2350e4a5d2b4f199144a098c8452edf"
521 | ],
522 | "layout": "IPY_MODEL_336ea372a26f40318a5efb5d3594ffbd",
523 | "left": 10097,
524 | "modisdate": "2023-01-10",
525 | "north": 38.45789034424927,
526 | "options": [
527 | "bounce_at_zoom_limits",
528 | "box_zoom",
529 | "center",
530 | "close_popup_on_click",
531 | "double_click_zoom",
532 | "dragging",
533 | "fullscreen",
534 | "inertia",
535 | "inertia_deceleration",
536 | "inertia_max_speed",
537 | "interpolation",
538 | "keyboard",
539 | "keyboard_pan_offset",
540 | "keyboard_zoom_offset",
541 | "max_zoom",
542 | "min_zoom",
543 | "prefer_canvas",
544 | "scroll_wheel_zoom",
545 | "tap",
546 | "tap_tolerance",
547 | "touch_zoom",
548 | "world_copy_jump",
549 | "zoom",
550 | "zoom_animation_threshold",
551 | "zoom_delta",
552 | "zoom_snap"
553 | ],
554 | "prefer_canvas": false,
555 | "right": 10987,
556 | "south": 36.71687068791304,
557 | "style": "IPY_MODEL_bf58ec5d94f4449d9e9aea3c82654540",
558 | "top": 25173,
559 | "west": -124.53552246093751,
560 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/index.ipynb",
561 | "zoom": 8
562 | }
563 | },
564 | "9abf5ae099f048f5beaeeba9754b737e": {
565 | "model_module": "jupyter-leaflet",
566 | "model_module_version": "^0.17",
567 | "model_name": "LeafletZoomControlModel",
568 | "state": {
569 | "_model_module_version": "^0.17",
570 | "_view_module_version": "^0.17",
571 | "options": [
572 | "position",
573 | "zoom_in_text",
574 | "zoom_in_title",
575 | "zoom_out_text",
576 | "zoom_out_title"
577 | ]
578 | }
579 | },
580 | "a0f91845ebba48dbbf3f6bdd79323d6d": {
581 | "model_module": "jupyter-leaflet",
582 | "model_module_version": "^0.17",
583 | "model_name": "LeafletAttributionControlModel",
584 | "state": {
585 | "_model_module_version": "^0.17",
586 | "_view_module_version": "^0.17",
587 | "options": [
588 | "position",
589 | "prefix"
590 | ],
591 | "position": "bottomright",
592 | "prefix": "ipyleaflet"
593 | }
594 | },
595 | "b2350e4a5d2b4f199144a098c8452edf": {
596 | "model_module": "jupyter-leaflet",
597 | "model_module_version": "^0.17",
598 | "model_name": "LeafletTileLayerModel",
599 | "state": {
600 | "_model_module_version": "^0.17",
601 | "_view_module_version": "^0.17",
602 | "name": "Landsat scene",
603 | "options": [
604 | "attribution",
605 | "bounds",
606 | "detect_retina",
607 | "max_native_zoom",
608 | "max_zoom",
609 | "min_native_zoom",
610 | "min_zoom",
611 | "no_wrap",
612 | "tile_size",
613 | "tms",
614 | "zoom_offset"
615 | ],
616 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/6937350118cc01964c2600788ca9670e-11210a861d8ab34a09eab48e6043fc82/tiles/{z}/{x}/{y}"
617 | }
618 | },
619 | "b305b8311ddd4319920041f8c51cdc69": {
620 | "model_module": "jupyter-leaflet",
621 | "model_module_version": "^0.17",
622 | "model_name": "LeafletMapModel",
623 | "state": {
624 | "_model_module_version": "^0.17",
625 | "_view_module_version": "^0.17",
626 | "bottom": 22595,
627 | "center": [
628 | 49.4,
629 | 2.3
630 | ],
631 | "controls": [
632 | "IPY_MODEL_2625695324c1472cbab709507ae2db40",
633 | "IPY_MODEL_a0f91845ebba48dbbf3f6bdd79323d6d",
634 | "IPY_MODEL_8e5a271e635948deb4e3362a6b1b91b2",
635 | "IPY_MODEL_1e1aec83e42045bf9ccff2e7ea1f3552"
636 | ],
637 | "default_style": "IPY_MODEL_29efe12ec397463da27a60cd3b558f3f",
638 | "dragging_style": "IPY_MODEL_5d27cf14d73a41aeab2ea720a257ca70",
639 | "east": 4.735107421875001,
640 | "fullscreen": false,
641 | "interpolation": "bilinear",
642 | "layers": [
643 | "IPY_MODEL_e60c35ff7a49458ca1dc33059ad86f72",
644 | "IPY_MODEL_51d3cd5c24bb40f48c1508c09a932f11"
645 | ],
646 | "layout": "IPY_MODEL_3c36a4f289d04fb2886e978be5b06a17",
647 | "left": 32744,
648 | "modisdate": "2023-01-10",
649 | "north": 50.11001070896015,
650 | "options": [
651 | "bounce_at_zoom_limits",
652 | "box_zoom",
653 | "center",
654 | "close_popup_on_click",
655 | "double_click_zoom",
656 | "dragging",
657 | "fullscreen",
658 | "inertia",
659 | "inertia_deceleration",
660 | "inertia_max_speed",
661 | "interpolation",
662 | "keyboard",
663 | "keyboard_pan_offset",
664 | "keyboard_zoom_offset",
665 | "max_zoom",
666 | "min_zoom",
667 | "prefer_canvas",
668 | "scroll_wheel_zoom",
669 | "tap",
670 | "tap_tolerance",
671 | "touch_zoom",
672 | "world_copy_jump",
673 | "zoom",
674 | "zoom_animation_threshold",
675 | "zoom_delta",
676 | "zoom_snap"
677 | ],
678 | "prefer_canvas": false,
679 | "right": 33630,
680 | "south": 48.680080770292875,
681 | "style": "IPY_MODEL_f110c4c3bbe345469b89eebd1016fef0",
682 | "top": 22195,
683 | "west": -0.13183593750000003,
684 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/index.ipynb",
685 | "zoom": 8
686 | }
687 | },
688 | "bf58ec5d94f4449d9e9aea3c82654540": {
689 | "model_module": "jupyter-leaflet",
690 | "model_module_version": "^0.17",
691 | "model_name": "LeafletMapStyleModel",
692 | "state": {
693 | "_model_module_version": "^0.17"
694 | }
695 | },
696 | "c0e0a057b273444c867bb91d3d2b8166": {
697 | "model_module": "@jupyter-widgets/controls",
698 | "model_module_version": "2.0.0",
699 | "model_name": "ButtonStyleModel",
700 | "state": {
701 | "font_family": null,
702 | "font_size": null,
703 | "font_style": null,
704 | "font_variant": null,
705 | "font_weight": null,
706 | "text_color": null,
707 | "text_decoration": null
708 | }
709 | },
710 | "c2ab078daf744726a06e51b73d99e2ee": {
711 | "model_module": "jupyter-leaflet",
712 | "model_module_version": "^0.17",
713 | "model_name": "LeafletLayersControlModel",
714 | "state": {
715 | "_model_module_version": "^0.17",
716 | "_view_module_version": "^0.17",
717 | "options": [
718 | "position"
719 | ],
720 | "position": "topright"
721 | }
722 | },
723 | "c53792be7e3c4833b3dc6315bbfba4b4": {
724 | "model_module": "ipytree",
725 | "model_module_version": "^0.2",
726 | "model_name": "TreeModel",
727 | "state": {
728 | "_model_module_version": "^0.2",
729 | "_view_module_version": "^0.2",
730 | "layout": "IPY_MODEL_4f8b472c81eb412f9f90be778d5b046f",
731 | "nodes": [
732 | "IPY_MODEL_2319a7c35eb146fab3231d5b3c2d223f"
733 | ]
734 | }
735 | },
736 | "e60c35ff7a49458ca1dc33059ad86f72": {
737 | "model_module": "jupyter-leaflet",
738 | "model_module_version": "^0.17",
739 | "model_name": "LeafletTileLayerModel",
740 | "state": {
741 | "_model_module_version": "^0.17",
742 | "_view_module_version": "^0.17",
743 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
744 | "base": true,
745 | "max_zoom": 16,
746 | "min_zoom": 1,
747 | "name": "Stamen.Watercolor",
748 | "options": [
749 | "attribution",
750 | "bounds",
751 | "detect_retina",
752 | "max_native_zoom",
753 | "max_zoom",
754 | "min_native_zoom",
755 | "min_zoom",
756 | "no_wrap",
757 | "tile_size",
758 | "tms",
759 | "zoom_offset"
760 | ],
761 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
762 | }
763 | },
764 | "e63129a6b6214b86b55161f9b3b01707": {
765 | "model_module": "@jupyter-widgets/controls",
766 | "model_module_version": "2.0.0",
767 | "model_name": "ButtonModel",
768 | "state": {
769 | "icon": "info-circle 2x fw inverse",
770 | "layout": "IPY_MODEL_505a42fde6424bdfb971ae73d8a26c74",
771 | "style": "IPY_MODEL_c0e0a057b273444c867bb91d3d2b8166",
772 | "tooltip": null
773 | }
774 | },
775 | "f110c4c3bbe345469b89eebd1016fef0": {
776 | "model_module": "jupyter-leaflet",
777 | "model_module_version": "^0.17",
778 | "model_name": "LeafletMapStyleModel",
779 | "state": {
780 | "_model_module_version": "^0.17"
781 | }
782 | }
783 | },
784 | "version_major": 2,
785 | "version_minor": 0
786 | }
787 | }
788 | },
789 | "nbformat": 4,
790 | "nbformat_minor": 4
791 | }
792 |
--------------------------------------------------------------------------------
/nbs/01_ipyleaflet.ipynb:
--------------------------------------------------------------------------------
1 | {
2 | "cells": [
3 | {
4 | "cell_type": "code",
5 | "execution_count": null,
6 | "id": "a299e0c1-82bf-4290-aab9-38397b05b533",
7 | "metadata": {},
8 | "outputs": [],
9 | "source": [
10 | "#|hide\n",
11 | "# Copyright 2022 Google LLC\n",
12 | "#\n",
13 | "# Licensed under the Apache License, Version 2.0 (the \"License\");\n",
14 | "# you may not use this file except in compliance with the License.\n",
15 | "# You may obtain a copy of the License at\n",
16 | "# \n",
17 | "# https://www.apache.org/licenses/LICENSE-2.0\n",
18 | "# \n",
19 | "# Unless required by applicable law or agreed to in writing, software\n",
20 | "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
21 | "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
22 | "# See the License for the specific language governing permissions and\n",
23 | "# limitations under the License."
24 | ]
25 | },
26 | {
27 | "cell_type": "code",
28 | "execution_count": null,
29 | "id": "93bd9280-7fdc-47b3-9ab6-e5892cbc8a4b",
30 | "metadata": {},
31 | "outputs": [],
32 | "source": [
33 | "#| default_exp ipyleaflet"
34 | ]
35 | },
36 | {
37 | "cell_type": "markdown",
38 | "id": "00063c01-2b45-4a56-b50a-aa20d10f4803",
39 | "metadata": {},
40 | "source": [
41 | "# ipyleaflet module\n",
42 | "\n",
43 | "> Extends ipyleaflet objects to work with Earth Engine generated data."
44 | ]
45 | },
46 | {
47 | "cell_type": "code",
48 | "execution_count": null,
49 | "id": "3d11593e-2715-4345-8831-2b1ceccac939",
50 | "metadata": {},
51 | "outputs": [],
52 | "source": [
53 | "#|hide\n",
54 | "from nbdev.showdoc import *"
55 | ]
56 | },
57 | {
58 | "cell_type": "code",
59 | "execution_count": null,
60 | "id": "20e9ed75-20a9-43a8-9ae8-9232ed911f9d",
61 | "metadata": {},
62 | "outputs": [],
63 | "source": [
64 | "#|export\n",
65 | "import ee\n",
66 | "import json\n",
67 | "import ipyleaflet\n",
68 | "import ipytree\n",
69 | "import ipywidgets as widgets\n",
70 | "import logging\n",
71 | "import traitlets"
72 | ]
73 | },
74 | {
75 | "cell_type": "code",
76 | "execution_count": null,
77 | "id": "c7082e8a-0627-4f05-836c-84cb1ad63fe5",
78 | "metadata": {},
79 | "outputs": [],
80 | "source": [
81 | "#|export\n",
82 | "logger = logging.getLogger()\n",
83 | "logger.setLevel(logging.WARNING)"
84 | ]
85 | },
86 | {
87 | "cell_type": "markdown",
88 | "id": "0a1fbd0b-4b5d-493b-bda2-9aa0603da7d1",
89 | "metadata": {},
90 | "source": [
91 | "## Map"
92 | ]
93 | },
94 | {
95 | "cell_type": "code",
96 | "execution_count": null,
97 | "id": "c34615c6-17a5-476e-89ff-9b195f8564ed",
98 | "metadata": {},
99 | "outputs": [],
100 | "source": [
101 | "#|export\n",
102 | "DEFAULT_MAP_HEIGHT = '400px'\n",
103 | "\n",
104 | "class TileLayerEE(ipyleaflet.TileLayer):\n",
105 | " \"\"\"Class for a tile layer generated by Earth Engine.\n",
106 | " \n",
107 | " Attributes:\n",
108 | " ee_object: An Earth Engine object.\n",
109 | " \"\"\"\n",
110 | " def __init__(self,\n",
111 | " ee_object:ee.Image, # An Earth Engine Image object\n",
112 | " *args,\n",
113 | " **kwargs):\n",
114 | "\n",
115 | " self.ee_object = ee_object\n",
116 | "\n",
117 | " super(TileLayerEE, self).__init__(*args, **kwargs)\n",
118 | "\n",
119 | "\n",
120 | "class Map(ipyleaflet.Map):\n",
121 | " \"\"\"An interactive map class for Jupyter clients.\n",
122 | " \n",
123 | " Attributes:\n",
124 | " layers_control: a boolean indicating whether to display a layers control.\n",
125 | " \"\"\"\n",
126 | "\n",
127 | " layers_control = traitlets.Bool(True)\n",
128 | "\n",
129 | " def __init__(self, *args, **kwargs):\n",
130 | "\n",
131 | " self.layers_control_instance = None\n",
132 | "\n",
133 | " # Set default values for the map.\n",
134 | " if 'zoom' not in kwargs:\n",
135 | " kwargs['zoom'] = 4\n",
136 | " \n",
137 | " if 'basemap' not in kwargs:\n",
138 | " kwargs['basemap'] = ipyleaflet.basemap_to_tiles(ipyleaflet.basemaps.Stamen.Watercolor)\n",
139 | "\n",
140 | " if 'height' not in kwargs:\n",
141 | " kwargs['height'] = DEFAULT_MAP_HEIGHT\n",
142 | " \n",
143 | " super(Map, self).__init__(*args, **kwargs)\n",
144 | " \n",
145 | " if self.layers_control:\n",
146 | " self.layers_control_instance = ipyleaflet.LayersControl(position='topright')\n",
147 | " self.add_control(self.layers_control_instance)\n",
148 | " \n",
149 | " self.default_style.cursor = 'crosshair'\n",
150 | " \n",
151 | " self.layout.width = '100%'\n",
152 | " \n",
153 | " \n",
154 | " def addLayer(self, eeObject, visParams={}, name=None, shown=True, opacity=1):\n",
155 | " \"\"\"Adds a layer for an Earth Engine object.\"\"\"\n",
156 | " \n",
157 | " if name is None:\n",
158 | " # Provide a default name for the layer in the form \"Layer ##\"\n",
159 | " name = f'Layer {len(self.layers)}'\n",
160 | "\n",
161 | " def get_tile_layer_url(ee_image_object):\n",
162 | " map_id_dict = ee.Image(ee_image_object).getMapId()\n",
163 | " return map_id_dict['tile_fetcher'].url_format\n",
164 | "\n",
165 | " # Assume that the eeObject is an ee.Image.\n",
166 | " # TODO: Generalize this to other EE objects.\n",
167 | " ee_image = eeObject\n",
168 | "\n",
169 | " tile_url = get_tile_layer_url(\n",
170 | " ee_image.visualize(**visParams)\n",
171 | " )\n",
172 | " self.add_layer(TileLayerEE(ee_object=eeObject, url=tile_url, name=name, visible=shown))"
173 | ]
174 | },
175 | {
176 | "cell_type": "markdown",
177 | "id": "4483948b-28c0-4869-bab6-2e2aad9b1db4",
178 | "metadata": {},
179 | "source": [
180 | "Display the default interactive map."
181 | ]
182 | },
183 | {
184 | "cell_type": "code",
185 | "execution_count": null,
186 | "id": "4e7223f7-4446-4ce2-9ffe-cd255617ff93",
187 | "metadata": {},
188 | "outputs": [],
189 | "source": [
190 | "map = Map()\n",
191 | "map"
192 | ]
193 | },
194 | {
195 | "cell_type": "markdown",
196 | "id": "e9efdbb6-6bc8-462b-87e0-6f370c8e198e",
197 | "metadata": {},
198 | "source": [
199 | "Display an Earth Engine raster layer."
200 | ]
201 | },
202 | {
203 | "cell_type": "code",
204 | "execution_count": null,
205 | "id": "5cea2a36-e760-4d6f-948d-fedbafcf4bf0",
206 | "metadata": {},
207 | "outputs": [],
208 | "source": [
209 | "ee.Initialize()"
210 | ]
211 | },
212 | {
213 | "cell_type": "code",
214 | "execution_count": null,
215 | "id": "f99b3d91-8e37-4d36-8c6d-8bbc3d43c91a",
216 | "metadata": {},
217 | "outputs": [],
218 | "source": [
219 | "dem = ee.Image('USGS/SRTMGL1_003')\n",
220 | "sample_image = dem.updateMask(dem.gt(0))\n",
221 | "\n",
222 | "map2 = Map()\n",
223 | "map2.addLayer(sample_image, {'min':0, 'max':4000}, name=\"My layer\", shown=True)\n",
224 | "map2"
225 | ]
226 | },
227 | {
228 | "cell_type": "markdown",
229 | "id": "83fb71ea-2e64-4506-8699-b1a0a75bfe8f",
230 | "metadata": {},
231 | "source": [
232 | "## Map Inspector"
233 | ]
234 | },
235 | {
236 | "cell_type": "markdown",
237 | "id": "d8cc2f1d-0df7-4ff0-b54d-b37790593f27",
238 | "metadata": {},
239 | "source": [
240 | "The following defines a `StructureTree` class, which builds a tree structure corresponding to data structure provided as input. The tree branches are lazily populated as the tree is expanded, so that large data structures can be efficiently rendered."
241 | ]
242 | },
243 | {
244 | "cell_type": "code",
245 | "execution_count": null,
246 | "id": "32ba4716-701d-4a0b-9f5c-16e79b0db054",
247 | "metadata": {},
248 | "outputs": [],
249 | "source": [
250 | "#|export\n",
251 | "class StructureTree(ipytree.Tree):\n",
252 | "\n",
253 | " JSON_PREFIX = 'JSON: '\n",
254 | " \n",
255 | " def __init__(self, data, **kwargs):\n",
256 | " self.data = data\n",
257 | " super().__init__(StructureTree.__ipytreeify(data))\n",
258 | " \n",
259 | " @staticmethod\n",
260 | " def __ipytreeify(data) -> tuple:\n",
261 | " \"\"\"Return a sequence of nodes\"\"\"\n",
262 | "\n",
263 | " def is_json(in_str):\n",
264 | " '''Determines if a string is JSON.'''\n",
265 | " return in_str.startswith(StructureTree.JSON_PREFIX)\n",
266 | "\n",
267 | " def inline_style(x, color='blue'):\n",
268 | " '''Wrap a string with inline HTML styling.'''\n",
269 | " return f'{x}'\n",
270 | " \n",
271 | " def handle_node_open(change):\n",
272 | " if change['new']:\n",
273 | " nodes_unpacked = []\n",
274 | " for node in change['owner'].nodes:\n",
275 | " # If there no subnodes, try to populate the subnodes.\n",
276 | " if len(node.nodes) == 0:\n",
277 | " if is_json(node.name):\n",
278 | " unpacked_json = json.loads(node.name[len(StructureTree.JSON_PREFIX):])\n",
279 | " if isinstance(unpacked_json, list):\n",
280 | " nodes_unpacked = StructureTree.__ipytreeify(unpacked_json)\n",
281 | " elif isinstance(unpacked_json, dict):\n",
282 | " nodes_unpacked = StructureTree.__ipytreeify(unpacked_json)\n",
283 | " else:\n",
284 | " raise\n",
285 | " else: # ~is_json(node.name)\n",
286 | " nodes_unpacked.append(node)\n",
287 | " change['owner'].nodes = nodes_unpacked \n",
288 | " \n",
289 | " if isinstance(data, list):\n",
290 | " node_list = []\n",
291 | " for count, el in enumerate(data):\n",
292 | " if isinstance(el, list):\n",
293 | " subnode = ipytree.Node(\n",
294 | " name=f'{inline_style(\"List\")} ({len(el)} elements)',\n",
295 | " nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(el)}')],\n",
296 | " opened=False)\n",
297 | " subnode.observe(handle_node_open, names='opened')\n",
298 | " elif isinstance(el, dict):\n",
299 | " subnode = ipytree.Node(\n",
300 | " name=f'{inline_style(\"Object\")} ({len(el)} elements)',\n",
301 | " nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(el)}')],\n",
302 | " opened=False)\n",
303 | " subnode.observe(handle_node_open, names='opened') \n",
304 | " else:\n",
305 | " subnode = ipytree.Node(f'{el}')\n",
306 | " node_list.append(subnode)\n",
307 | " return node_list\n",
308 | " elif isinstance(data, dict):\n",
309 | " node_list = []\n",
310 | " for key, value in data.items():\n",
311 | " if isinstance(value, list):\n",
312 | " subnode = ipytree.Node(\n",
313 | " name=f'{inline_style(key)}: List ({len(value)} elements)',\n",
314 | " nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(value)}')],\n",
315 | " opened=False)\n",
316 | " subnode.observe(handle_node_open, names='opened') \n",
317 | " elif isinstance(value, dict):\n",
318 | " subnode = ipytree.Node(\n",
319 | " name=f'{inline_style(key)}: Object ({len(value)} elements)',\n",
320 | " nodes=[ipytree.Node(f'{StructureTree.JSON_PREFIX}{json.dumps(value)}')],\n",
321 | " opened=False)\n",
322 | " subnode.observe(handle_node_open, names='opened') \n",
323 | " else:\n",
324 | " subnode = ipytree.Node(f'{inline_style(key)}: {value}')\n",
325 | " node_list.append(subnode)\n",
326 | " return node_list\n",
327 | " else:\n",
328 | " return (data, )\n"
329 | ]
330 | },
331 | {
332 | "cell_type": "markdown",
333 | "id": "04e768bf-c32d-4fdf-9077-fd9cd1add7aa",
334 | "metadata": {},
335 | "source": [
336 | "Here is a `StructureTree` object that demonstrates how lists, dictionaries, and nested objects are rendered:"
337 | ]
338 | },
339 | {
340 | "cell_type": "code",
341 | "execution_count": null,
342 | "id": "09c534ab-3d4d-4811-8427-3955954335d1",
343 | "metadata": {},
344 | "outputs": [],
345 | "source": [
346 | "st = StructureTree({'none': None,\n",
347 | " 'number': 1,\n",
348 | " 'string': 'hello!',\n",
349 | " 'list': [1, 'item1'], \n",
350 | " 'dictionary': {'a key': 'a value'},\n",
351 | " 'nesting': [\n",
352 | " ['nested', 'list'],\n",
353 | " {'a nested': 'dictionary'}\n",
354 | " ]})\n",
355 | "st"
356 | ]
357 | },
358 | {
359 | "cell_type": "markdown",
360 | "id": "e8be360e-8a7c-4485-9e62-172b27df8979",
361 | "metadata": {},
362 | "source": [
363 | "The following defines an `Inspector` class, which is used to display information obtained from querying a `Map` object."
364 | ]
365 | },
366 | {
367 | "cell_type": "code",
368 | "execution_count": null,
369 | "id": "14151217-fb5b-448c-826c-1a23e468361d",
370 | "metadata": {},
371 | "outputs": [],
372 | "source": [
373 | "#|export\n",
374 | "\n",
375 | "# Map scale at Level 0 in meters/pixel\n",
376 | "SCALE_LEVEL_0 = 156543.03392\n",
377 | "\n",
378 | "#class Inspector(ipytree.Tree):\n",
379 | "class Inspector(StructureTree): \n",
380 | " \"\"\"Class representing an inspector tool that responds to map events.\"\"\"\n",
381 | " \n",
382 | " def __init__(self,\n",
383 | " map_object=None, # An Earth Engine Image object\n",
384 | " *args,\n",
385 | " **kwargs):\n",
386 | " \n",
387 | " self.point_data = None\n",
388 | " # self.tree_data = None\n",
389 | " self.objects_data = {}\n",
390 | " self.pixels_data = {}\n",
391 | " \n",
392 | " self._disabled = False\n",
393 | " \n",
394 | " self.map_object = map_object\n",
395 | " self.layout.width = '100%'\n",
396 | " self.layout.max_height = '400px'\n",
397 | " self.layout.overflow = 'scroll'\n",
398 | "\n",
399 | " super(Inspector, self).__init__(\n",
400 | " data=['Click on the map to inspect the layers.'],\n",
401 | " *args, \n",
402 | " **kwargs\n",
403 | " )\n",
404 | "\n",
405 | " if map_object:\n",
406 | " self.set_map(map_object)\n",
407 | " \n",
408 | " # self.update_inspector()\n",
409 | " \n",
410 | " @property\n",
411 | " def point_node(self):\n",
412 | " return self.nodes[0]\n",
413 | " \n",
414 | " @point_node.setter\n",
415 | " def point_node(self, new_point_node):\n",
416 | " #(lat, lon) = new_coords\n",
417 | " _temp_nodes = list(self.nodes)\n",
418 | " _temp_nodes[0] = new_point_node\n",
419 | " self.nodes = _temp_nodes\n",
420 | " \n",
421 | " @property\n",
422 | " def pixels_node(self):\n",
423 | " return self.nodes[1]\n",
424 | " \n",
425 | " @property\n",
426 | " def objects_node(self):\n",
427 | " return self.nodes[2]\n",
428 | " \n",
429 | " @property\n",
430 | " def disabled(self):\n",
431 | " return self._disabled\n",
432 | " \n",
433 | " @disabled.setter\n",
434 | " def disabled(self, state):\n",
435 | " self._disabled = state\n",
436 | " \n",
437 | " def update_inspector(self, coords=None):\n",
438 | " \"\"\"Update information in the inspector tree.\"\"\"\n",
439 | " \n",
440 | " def _order_items(item_dict, ordering_list):\n",
441 | " \"\"\"Orders dictionary items in a specified order.\"\"\"\n",
442 | " list_of_tuples = [(key, item_dict[key]) for key in [x for x in ordering_list if x in item_dict.keys()]]\n",
443 | " return dict(list_of_tuples) \n",
444 | " \n",
445 | " if self.map_object:\n",
446 | " if not self.disabled and coords:\n",
447 | " \n",
448 | " self.nodes = [ipytree.Node('Loading...')]\n",
449 | " \n",
450 | " (lat, lon) = coords\n",
451 | "\n",
452 | " scale_approx = SCALE_LEVEL_0 / 2**self.map_object.zoom\n",
453 | " self.point_data = {\n",
454 | " 'Longitude': f'{lon:.6f}',\n",
455 | " 'Latitude': f'{lat:.6f}',\n",
456 | " 'Zoom Level': f'{self.map_object.zoom:.0f}',\n",
457 | " 'Scale (approx. m/px)': f'{scale_approx:.2f}'\n",
458 | " }\n",
459 | " \n",
460 | " # Update the Pixels folder\n",
461 | " for layer in self.map_object.layers:\n",
462 | " if not layer.base:\n",
463 | " ee_type = ee.Algorithms.ObjectType(layer.ee_object).getInfo()\n",
464 | "\n",
465 | " if ee_type == 'Image':\n",
466 | " value_dict = layer.ee_object.reduceRegion(\n",
467 | " reducer=ee.Reducer.mean(),\n",
468 | " geometry=ee.Geometry.Point(lon, lat),\n",
469 | " scale=scale_approx,\n",
470 | " bestEffort=True\n",
471 | " ).getInfo()\n",
472 | " num_bands = len(value_dict.keys())\n",
473 | " \n",
474 | " band_dict = {}\n",
475 | " has_unmasked_pixel = False\n",
476 | " for bandname in layer.ee_object.bandNames().getInfo(): \n",
477 | " if value_dict[bandname] is not None:\n",
478 | " has_unmasked_pixel = True\n",
479 | " band_dict.update({f'{bandname}': f'{value_dict[bandname]:.4f}'})\n",
480 | " else: \n",
481 | " band_dict.update({f'{bandname}': f'masked'})\n",
482 | " \n",
483 | " # if not has_unmasked_pixel:\n",
484 | " # layer_node.nodes = [\n",
485 | " # ipytree.Node(f'No unmasked pixels at clicked point.'),\n",
486 | " # ] \n",
487 | " # pixel_nodes.append(layer_node) \n",
488 | " self.pixels_data.update({f'{layer.name}': band_dict})\n",
489 | " \n",
490 | " # Update the Objects folder\n",
491 | " for layer in self.map_object.layers:\n",
492 | " if not layer.base:\n",
493 | " ee_type = ee.Algorithms.ObjectType(layer.ee_object).getInfo() \n",
494 | " layer_info = layer.ee_object.getInfo()\n",
495 | "\n",
496 | " # Order the layer information.\n",
497 | " ordering_list = ['type', 'id', 'version', 'bands', 'properties']\n",
498 | " layer_info = _order_items(layer_info, ordering_list)\n",
499 | " layer_dict = {f'{layer.name}': layer_info}\n",
500 | " \n",
501 | " self.objects_data.update(layer_dict)\n",
502 | " \n",
503 | " self.nodes = [\n",
504 | " ipytree.Node('Point', nodes=StructureTree(self.point_data).nodes),\n",
505 | " ipytree.Node('Pixels', nodes=StructureTree(self.pixels_data).nodes, opened=False),\n",
506 | " ipytree.Node('Objects', nodes=StructureTree(self.objects_data).nodes, opened=False),\n",
507 | " ] \n",
508 | " \n",
509 | " def register_map(self, map_object):\n",
510 | " def handle_interaction(type, event, coordinates):\n",
511 | " if type == 'click':\n",
512 | " self.update_inspector(coordinates)\n",
513 | " map_object.on_interaction(handle_interaction)\n",
514 | " \n",
515 | " def set_map(self, map_object):\n",
516 | " self.map_object = map_object\n",
517 | " self.register_map(map_object)\n",
518 | "\n",
519 | " def get_map(self):\n",
520 | " return self.map_object"
521 | ]
522 | },
523 | {
524 | "cell_type": "code",
525 | "execution_count": null,
526 | "id": "06a2a1e3-a6f4-445a-bfac-dbe84119d277",
527 | "metadata": {},
528 | "outputs": [],
529 | "source": [
530 | "# Testing it out...\n",
531 | "map1 = Map()\n",
532 | "map1.addLayer(ee.Image.pixelLonLat(), {'min':-90, 'max':90, 'opacity':0.5}, 'pixelLatLon')\n",
533 | "# map1.addLayer(ee.Image(\"CGIAR/SRTM90_V4\"), {'opacity':0.5}, 'SRTM')\n",
534 | "inspector1 = Inspector(map_object = map1)\n",
535 | "\n",
536 | "zoom_slider = widgets.IntSlider(description='Zoom level:', min=0, max=15, value=7)\n",
537 | "widget_control1 = ipyleaflet.WidgetControl(widget=inspector1,\n",
538 | " position='bottomright',\n",
539 | " layout=widgets.Layout(overflow='scroll'))\n",
540 | "map1.add_control(widget_control1)\n",
541 | "map1"
542 | ]
543 | },
544 | {
545 | "cell_type": "markdown",
546 | "id": "cda35d5d-e749-49b2-8e05-655d1e0e28eb",
547 | "metadata": {},
548 | "source": [
549 | "## Examples"
550 | ]
551 | },
552 | {
553 | "cell_type": "markdown",
554 | "id": "37a58bbd-fe2d-4561-a36b-0ff77d6bc60e",
555 | "metadata": {},
556 | "source": [
557 | "### Inspector object only"
558 | ]
559 | },
560 | {
561 | "cell_type": "code",
562 | "execution_count": null,
563 | "id": "f52e5a58-4855-4708-ab51-479ffc5e757b",
564 | "metadata": {},
565 | "outputs": [],
566 | "source": [
567 | "inspector1 = Inspector()\n",
568 | "inspector1"
569 | ]
570 | },
571 | {
572 | "cell_type": "markdown",
573 | "id": "d69651a9-cb29-4989-b51b-95078d584c68",
574 | "metadata": {},
575 | "source": [
576 | "### Inspector with a Map"
577 | ]
578 | },
579 | {
580 | "cell_type": "code",
581 | "execution_count": null,
582 | "id": "a544bd7f-489e-4a15-989f-7fb4eb77ed8d",
583 | "metadata": {},
584 | "outputs": [],
585 | "source": [
586 | "ee.Initialize()"
587 | ]
588 | },
589 | {
590 | "cell_type": "code",
591 | "execution_count": null,
592 | "id": "d8b89564-1107-4203-83cf-1109dc4da34a",
593 | "metadata": {},
594 | "outputs": [],
595 | "source": [
596 | "map = Map()\n",
597 | "inspector2 = Inspector(map_object=map)\n",
598 | "display(\n",
599 | " widgets.HBox([\n",
600 | " map,\n",
601 | " inspector2\n",
602 | " ],\n",
603 | " layout=widgets.Layout(border='1px solid black')))\n",
604 | "\n",
605 | "map.addLayer(ee.Image.pixelLonLat(), {'min':-90, 'max':90, 'opacity':0.5}, 'LonLat')\n",
606 | "map.addLayer(\n",
607 | " ee.Image('LANDSAT/LC09/C02/T1_L2/LC09_187058_20220105'),\n",
608 | " {'min':0, 'max':90, 'opacity':0.5},\n",
609 | " 'Landsat')"
610 | ]
611 | },
612 | {
613 | "cell_type": "code",
614 | "execution_count": null,
615 | "id": "2b56d9f5-ad90-4ecd-a122-652abed3c501",
616 | "metadata": {},
617 | "outputs": [],
618 | "source": [
619 | "#| hide\n",
620 | "import nbdev; nbdev.nbdev_export()"
621 | ]
622 | }
623 | ],
624 | "metadata": {
625 | "kernelspec": {
626 | "display_name": "Python 3 (ipykernel)",
627 | "language": "python",
628 | "name": "python3"
629 | },
630 | "widgets": {
631 | "application/vnd.jupyter.widget-state+json": {
632 | "state": {
633 | "07ae7d0268784ae1a137cb50a08767a3": {
634 | "model_module": "ipytree",
635 | "model_module_version": "^0.2",
636 | "model_name": "NodeModel",
637 | "state": {
638 | "_id": "4b83bc8a-326a-4de3-bba7-5588e394c1d4",
639 | "_model_module_version": "^0.2",
640 | "_view_module_version": "^0.2",
641 | "name": "number: 1"
642 | }
643 | },
644 | "0a0f61d71c324cce8a8724a97449ba20": {
645 | "model_module": "@jupyter-widgets/base",
646 | "model_module_version": "2.0.0",
647 | "model_name": "LayoutModel",
648 | "state": {}
649 | },
650 | "0dcf84bfbf3c42e9b71bdda3ad4f67f2": {
651 | "model_module": "jupyter-leaflet",
652 | "model_module_version": "^0.17",
653 | "model_name": "LeafletTileLayerModel",
654 | "state": {
655 | "_model_module_version": "^0.17",
656 | "_view_module_version": "^0.17",
657 | "name": "pixelLatLon",
658 | "options": [
659 | "attribution",
660 | "bounds",
661 | "detect_retina",
662 | "max_native_zoom",
663 | "max_zoom",
664 | "min_native_zoom",
665 | "min_zoom",
666 | "no_wrap",
667 | "tile_size",
668 | "tms",
669 | "zoom_offset"
670 | ],
671 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/94684f9069ec559fbd6d13a6e0b6ee1c-a6fd349ff172d45b9398f3efbb324a0a/tiles/{z}/{x}/{y}"
672 | }
673 | },
674 | "1488a7962d1b498785e5d342e1f9e145": {
675 | "model_module": "jupyter-leaflet",
676 | "model_module_version": "^0.17",
677 | "model_name": "LeafletAttributionControlModel",
678 | "state": {
679 | "_model_module_version": "^0.17",
680 | "_view_module_version": "^0.17",
681 | "options": [
682 | "position",
683 | "prefix"
684 | ],
685 | "position": "bottomright",
686 | "prefix": "ipyleaflet"
687 | }
688 | },
689 | "160f1f17eda549e794967ad36033535f": {
690 | "model_module": "jupyter-leaflet",
691 | "model_module_version": "^0.17",
692 | "model_name": "LeafletLayersControlModel",
693 | "state": {
694 | "_model_module_version": "^0.17",
695 | "_view_module_version": "^0.17",
696 | "options": [
697 | "position"
698 | ],
699 | "position": "topright"
700 | }
701 | },
702 | "198b0f2603dd44619a55e5835c97b8de": {
703 | "model_module": "@jupyter-widgets/controls",
704 | "model_module_version": "2.0.0",
705 | "model_name": "HBoxModel",
706 | "state": {
707 | "children": [
708 | "IPY_MODEL_f335e46a0a3a4446873a52efb6bd3d99",
709 | "IPY_MODEL_9f173486cd994f3ab7b1d4451a0a9ce3"
710 | ],
711 | "layout": "IPY_MODEL_782599a241fb435abac9527c114b1f98"
712 | }
713 | },
714 | "19e735ec76674632b8e00ded8400cdc7": {
715 | "model_module": "jupyter-leaflet",
716 | "model_module_version": "^0.17",
717 | "model_name": "LeafletLayersControlModel",
718 | "state": {
719 | "_model_module_version": "^0.17",
720 | "_view_module_version": "^0.17",
721 | "options": [
722 | "position"
723 | ],
724 | "position": "topright"
725 | }
726 | },
727 | "2230df77a880469cb030cb1cdd046a6b": {
728 | "model_module": "jupyter-leaflet",
729 | "model_module_version": "^0.17",
730 | "model_name": "LeafletZoomControlModel",
731 | "state": {
732 | "_model_module_version": "^0.17",
733 | "_view_module_version": "^0.17",
734 | "options": [
735 | "position",
736 | "zoom_in_text",
737 | "zoom_in_title",
738 | "zoom_out_text",
739 | "zoom_out_title"
740 | ]
741 | }
742 | },
743 | "2844db030e5d4cf48dc4184d9d199e01": {
744 | "model_module": "jupyter-leaflet",
745 | "model_module_version": "^0.17",
746 | "model_name": "LeafletMapStyleModel",
747 | "state": {
748 | "_model_module_version": "^0.17"
749 | }
750 | },
751 | "2d81e9ccd8c14032b275563f33f194e2": {
752 | "model_module": "jupyter-leaflet",
753 | "model_module_version": "^0.17",
754 | "model_name": "LeafletAttributionControlModel",
755 | "state": {
756 | "_model_module_version": "^0.17",
757 | "_view_module_version": "^0.17",
758 | "options": [
759 | "position",
760 | "prefix"
761 | ],
762 | "position": "bottomright",
763 | "prefix": "ipyleaflet"
764 | }
765 | },
766 | "324543c15533487ba043c58c913c23cf": {
767 | "model_module": "jupyter-leaflet",
768 | "model_module_version": "^0.17",
769 | "model_name": "LeafletZoomControlModel",
770 | "state": {
771 | "_model_module_version": "^0.17",
772 | "_view_module_version": "^0.17",
773 | "options": [
774 | "position",
775 | "zoom_in_text",
776 | "zoom_in_title",
777 | "zoom_out_text",
778 | "zoom_out_title"
779 | ]
780 | }
781 | },
782 | "33a61cf764cb4927920c8222e90ccb60": {
783 | "model_module": "jupyter-leaflet",
784 | "model_module_version": "^0.17",
785 | "model_name": "LeafletTileLayerModel",
786 | "state": {
787 | "_model_module_version": "^0.17",
788 | "_view_module_version": "^0.17",
789 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
790 | "base": true,
791 | "max_zoom": 16,
792 | "min_zoom": 1,
793 | "name": "Stamen.Watercolor",
794 | "options": [
795 | "attribution",
796 | "bounds",
797 | "detect_retina",
798 | "max_native_zoom",
799 | "max_zoom",
800 | "min_native_zoom",
801 | "min_zoom",
802 | "no_wrap",
803 | "tile_size",
804 | "tms",
805 | "zoom_offset"
806 | ],
807 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
808 | }
809 | },
810 | "34eccbbe46bb4e7582ff83f2167a587d": {
811 | "model_module": "jupyter-leaflet",
812 | "model_module_version": "^0.17",
813 | "model_name": "LeafletAttributionControlModel",
814 | "state": {
815 | "_model_module_version": "^0.17",
816 | "_view_module_version": "^0.17",
817 | "options": [
818 | "position",
819 | "prefix"
820 | ],
821 | "position": "bottomright",
822 | "prefix": "ipyleaflet"
823 | }
824 | },
825 | "3b75beaea6204c1ab2773feba0fc4f44": {
826 | "model_module": "jupyter-leaflet",
827 | "model_module_version": "^0.17",
828 | "model_name": "LeafletMapModel",
829 | "state": {
830 | "_model_module_version": "^0.17",
831 | "_view_module_version": "^0.17",
832 | "bottom": 2248,
833 | "controls": [
834 | "IPY_MODEL_324543c15533487ba043c58c913c23cf",
835 | "IPY_MODEL_1488a7962d1b498785e5d342e1f9e145",
836 | "IPY_MODEL_19e735ec76674632b8e00ded8400cdc7",
837 | "IPY_MODEL_3f213aba22b14136a5b821ca7259d3fc"
838 | ],
839 | "default_style": "IPY_MODEL_91f2a10b7d3b448988309cca16a310d7",
840 | "dragging_style": "IPY_MODEL_d0d292fd509a42199487515df00981f0",
841 | "east": 83.05664062500001,
842 | "fullscreen": false,
843 | "interpolation": "bilinear",
844 | "layers": [
845 | "IPY_MODEL_f25225841c5d4e5ca0c9f57ba5f01c4a",
846 | "IPY_MODEL_0dcf84bfbf3c42e9b71bdda3ad4f67f2"
847 | ],
848 | "layout": "IPY_MODEL_54ad4bc247f0492cb40132748c9b3e7d",
849 | "left": 1104,
850 | "modisdate": "2023-01-10",
851 | "north": 17.308687886770034,
852 | "options": [
853 | "bounce_at_zoom_limits",
854 | "box_zoom",
855 | "center",
856 | "close_popup_on_click",
857 | "double_click_zoom",
858 | "dragging",
859 | "fullscreen",
860 | "inertia",
861 | "inertia_deceleration",
862 | "inertia_max_speed",
863 | "interpolation",
864 | "keyboard",
865 | "keyboard_pan_offset",
866 | "keyboard_zoom_offset",
867 | "max_zoom",
868 | "min_zoom",
869 | "prefer_canvas",
870 | "scroll_wheel_zoom",
871 | "tap",
872 | "tap_tolerance",
873 | "touch_zoom",
874 | "world_copy_jump",
875 | "zoom",
876 | "zoom_animation_threshold",
877 | "zoom_delta",
878 | "zoom_snap"
879 | ],
880 | "prefer_canvas": false,
881 | "right": 2993,
882 | "south": -17.308687886770024,
883 | "style": "IPY_MODEL_aa7270cfa51341e2ba6ac0c04873ca0c",
884 | "top": 1848,
885 | "west": -82.96875000000001,
886 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/01_ipyleaflet.ipynb",
887 | "zoom": 4
888 | }
889 | },
890 | "3cd16605239f401f80016b0709e90fd0": {
891 | "model_module": "jupyter-leaflet",
892 | "model_module_version": "^0.17",
893 | "model_name": "LeafletMapStyleModel",
894 | "state": {
895 | "_model_module_version": "^0.17",
896 | "cursor": "crosshair"
897 | }
898 | },
899 | "3f213aba22b14136a5b821ca7259d3fc": {
900 | "model_module": "jupyter-leaflet",
901 | "model_module_version": "^0.17",
902 | "model_name": "LeafletWidgetControlModel",
903 | "state": {
904 | "_model_module": "jupyter-leaflet",
905 | "_model_module_version": "^0.17",
906 | "_view_count": null,
907 | "_view_module": "jupyter-leaflet",
908 | "_view_module_version": "^0.17",
909 | "options": [
910 | "position",
911 | "transparent_bg"
912 | ],
913 | "position": "bottomright",
914 | "widget": "IPY_MODEL_6044e7e5893e4994a9d1bbdb0d17db25"
915 | }
916 | },
917 | "46ec4ffb94c54ddf9b7a01b8567ef4e5": {
918 | "model_module": "@jupyter-widgets/base",
919 | "model_module_version": "2.0.0",
920 | "model_name": "LayoutModel",
921 | "state": {
922 | "width": "100%"
923 | }
924 | },
925 | "47097130e74244829c4536b3a89300b6": {
926 | "model_module": "ipytree",
927 | "model_module_version": "^0.2",
928 | "model_name": "NodeModel",
929 | "state": {
930 | "_id": "f4deb5e0-ea37-4574-94e9-b3143a3e65af",
931 | "_model_module_version": "^0.2",
932 | "_view_module_version": "^0.2",
933 | "name": "none: None"
934 | }
935 | },
936 | "4e33a3e56de54a788e6516a373dd16cb": {
937 | "model_module": "jupyter-leaflet",
938 | "model_module_version": "^0.17",
939 | "model_name": "LeafletTileLayerModel",
940 | "state": {
941 | "_model_module_version": "^0.17",
942 | "_view_module_version": "^0.17",
943 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
944 | "base": true,
945 | "max_zoom": 16,
946 | "min_zoom": 1,
947 | "name": "Stamen.Watercolor",
948 | "options": [
949 | "attribution",
950 | "bounds",
951 | "detect_retina",
952 | "max_native_zoom",
953 | "max_zoom",
954 | "min_native_zoom",
955 | "min_zoom",
956 | "no_wrap",
957 | "tile_size",
958 | "tms",
959 | "zoom_offset"
960 | ],
961 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
962 | }
963 | },
964 | "51dc8cde17a84b6897839090ec65fa08": {
965 | "model_module": "jupyter-leaflet",
966 | "model_module_version": "^0.17",
967 | "model_name": "LeafletMapStyleModel",
968 | "state": {
969 | "_model_module_version": "^0.17",
970 | "cursor": "crosshair"
971 | }
972 | },
973 | "530c702061914f549aa756a040896abb": {
974 | "model_module": "jupyter-leaflet",
975 | "model_module_version": "^0.17",
976 | "model_name": "LeafletMapStyleModel",
977 | "state": {
978 | "_model_module_version": "^0.17"
979 | }
980 | },
981 | "54732d9ee511484c86c640b59f9d1f61": {
982 | "model_module": "jupyter-leaflet",
983 | "model_module_version": "^0.17",
984 | "model_name": "LeafletZoomControlModel",
985 | "state": {
986 | "_model_module_version": "^0.17",
987 | "_view_module_version": "^0.17",
988 | "options": [
989 | "position",
990 | "zoom_in_text",
991 | "zoom_in_title",
992 | "zoom_out_text",
993 | "zoom_out_title"
994 | ]
995 | }
996 | },
997 | "54ad4bc247f0492cb40132748c9b3e7d": {
998 | "model_module": "@jupyter-widgets/base",
999 | "model_module_version": "2.0.0",
1000 | "model_name": "LayoutModel",
1001 | "state": {
1002 | "width": "100%"
1003 | }
1004 | },
1005 | "5d3303f3faf04d21b9d362a4d4d77d75": {
1006 | "model_module": "ipytree",
1007 | "model_module_version": "^0.2",
1008 | "model_name": "NodeModel",
1009 | "state": {
1010 | "_id": "1e586d04-c1dc-4c33-9da4-3cb1b4f9939d",
1011 | "_model_module_version": "^0.2",
1012 | "_view_module_version": "^0.2",
1013 | "name": "Click on the map to inspect the layers."
1014 | }
1015 | },
1016 | "6044e7e5893e4994a9d1bbdb0d17db25": {
1017 | "model_module": "ipytree",
1018 | "model_module_version": "^0.2",
1019 | "model_name": "TreeModel",
1020 | "state": {
1021 | "_model_module_version": "^0.2",
1022 | "_view_module_version": "^0.2",
1023 | "layout": "IPY_MODEL_8b4f1b315674492aa2aa0304fb09dbe9",
1024 | "nodes": [
1025 | "IPY_MODEL_b5842c361b8a4929ac798f49ebbf8216"
1026 | ]
1027 | }
1028 | },
1029 | "67489456c53340f980a0bead87bddeea": {
1030 | "model_module": "ipytree",
1031 | "model_module_version": "^0.2",
1032 | "model_name": "NodeModel",
1033 | "state": {
1034 | "_id": "b905e615-a770-4437-839d-075958201131",
1035 | "_model_module_version": "^0.2",
1036 | "_view_module_version": "^0.2",
1037 | "name": "dictionary: Object (1 elements)",
1038 | "nodes": [
1039 | "IPY_MODEL_f2c6e70ba9084957aa70d6e157e77c0e"
1040 | ],
1041 | "opened": false
1042 | }
1043 | },
1044 | "68f7ab1013b843a0a0bc5ee31c5e93ca": {
1045 | "model_module": "@jupyter-widgets/base",
1046 | "model_module_version": "2.0.0",
1047 | "model_name": "LayoutModel",
1048 | "state": {
1049 | "overflow": "scroll"
1050 | }
1051 | },
1052 | "6a2efe109de649ceb140ff466c5ee86f": {
1053 | "model_module": "jupyter-leaflet",
1054 | "model_module_version": "^0.17",
1055 | "model_name": "LeafletTileLayerModel",
1056 | "state": {
1057 | "_model_module_version": "^0.17",
1058 | "_view_module_version": "^0.17",
1059 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
1060 | "base": true,
1061 | "max_zoom": 16,
1062 | "min_zoom": 1,
1063 | "name": "Stamen.Watercolor",
1064 | "options": [
1065 | "attribution",
1066 | "bounds",
1067 | "detect_retina",
1068 | "max_native_zoom",
1069 | "max_zoom",
1070 | "min_native_zoom",
1071 | "min_zoom",
1072 | "no_wrap",
1073 | "tile_size",
1074 | "tms",
1075 | "zoom_offset"
1076 | ],
1077 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
1078 | }
1079 | },
1080 | "6f2ea328676a49879861b6b4bcc561d7": {
1081 | "model_module": "jupyter-leaflet",
1082 | "model_module_version": "^0.17",
1083 | "model_name": "LeafletMapStyleModel",
1084 | "state": {
1085 | "_model_module_version": "^0.17"
1086 | }
1087 | },
1088 | "77f396b373cf41c3a590a7e5f445813a": {
1089 | "model_module": "jupyter-leaflet",
1090 | "model_module_version": "^0.17",
1091 | "model_name": "LeafletZoomControlModel",
1092 | "state": {
1093 | "_model_module_version": "^0.17",
1094 | "_view_module_version": "^0.17",
1095 | "options": [
1096 | "position",
1097 | "zoom_in_text",
1098 | "zoom_in_title",
1099 | "zoom_out_text",
1100 | "zoom_out_title"
1101 | ]
1102 | }
1103 | },
1104 | "782599a241fb435abac9527c114b1f98": {
1105 | "model_module": "@jupyter-widgets/base",
1106 | "model_module_version": "2.0.0",
1107 | "model_name": "LayoutModel",
1108 | "state": {
1109 | "border_bottom": "1px solid black",
1110 | "border_left": "1px solid black",
1111 | "border_right": "1px solid black",
1112 | "border_top": "1px solid black"
1113 | }
1114 | },
1115 | "798b4009a670401eb10bb0cd4a2d5902": {
1116 | "model_module": "jupyter-leaflet",
1117 | "model_module_version": "^0.17",
1118 | "model_name": "LeafletTileLayerModel",
1119 | "state": {
1120 | "_model_module_version": "^0.17",
1121 | "_view_module_version": "^0.17",
1122 | "name": "LonLat",
1123 | "options": [
1124 | "attribution",
1125 | "bounds",
1126 | "detect_retina",
1127 | "max_native_zoom",
1128 | "max_zoom",
1129 | "min_native_zoom",
1130 | "min_zoom",
1131 | "no_wrap",
1132 | "tile_size",
1133 | "tms",
1134 | "zoom_offset"
1135 | ],
1136 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/94684f9069ec559fbd6d13a6e0b6ee1c-04378696088ee9cf631f26a06269232d/tiles/{z}/{x}/{y}"
1137 | }
1138 | },
1139 | "8473a051cc414f46b4d978eaa18859eb": {
1140 | "model_module": "jupyter-leaflet",
1141 | "model_module_version": "^0.17",
1142 | "model_name": "LeafletMapStyleModel",
1143 | "state": {
1144 | "_model_module_version": "^0.17",
1145 | "cursor": "crosshair"
1146 | }
1147 | },
1148 | "849b7220f4514883a28ad460340c4cac": {
1149 | "model_module": "jupyter-leaflet",
1150 | "model_module_version": "^0.17",
1151 | "model_name": "LeafletMapModel",
1152 | "state": {
1153 | "_model_module_version": "^0.17",
1154 | "_view_module_version": "^0.17",
1155 | "bottom": 2248,
1156 | "controls": [
1157 | "IPY_MODEL_77f396b373cf41c3a590a7e5f445813a",
1158 | "IPY_MODEL_ee54cabd174f47ae94402d42c8a5c41b",
1159 | "IPY_MODEL_c0c3c520dd564b9a990b367c80d02320"
1160 | ],
1161 | "default_style": "IPY_MODEL_8473a051cc414f46b4d978eaa18859eb",
1162 | "dragging_style": "IPY_MODEL_c7fb9ecc8e80462e9b9bf33a74800c51",
1163 | "east": 83.05664062500001,
1164 | "fullscreen": false,
1165 | "interpolation": "bilinear",
1166 | "layers": [
1167 | "IPY_MODEL_6a2efe109de649ceb140ff466c5ee86f",
1168 | "IPY_MODEL_f0c9cd6fe4a84c5db48cb2e45895a965"
1169 | ],
1170 | "layout": "IPY_MODEL_d0b665c8f34149b59bab95267946ec4b",
1171 | "left": 1104,
1172 | "modisdate": "2023-01-10",
1173 | "north": 17.308687886770034,
1174 | "options": [
1175 | "bounce_at_zoom_limits",
1176 | "box_zoom",
1177 | "center",
1178 | "close_popup_on_click",
1179 | "double_click_zoom",
1180 | "dragging",
1181 | "fullscreen",
1182 | "inertia",
1183 | "inertia_deceleration",
1184 | "inertia_max_speed",
1185 | "interpolation",
1186 | "keyboard",
1187 | "keyboard_pan_offset",
1188 | "keyboard_zoom_offset",
1189 | "max_zoom",
1190 | "min_zoom",
1191 | "prefer_canvas",
1192 | "scroll_wheel_zoom",
1193 | "tap",
1194 | "tap_tolerance",
1195 | "touch_zoom",
1196 | "world_copy_jump",
1197 | "zoom",
1198 | "zoom_animation_threshold",
1199 | "zoom_delta",
1200 | "zoom_snap"
1201 | ],
1202 | "prefer_canvas": false,
1203 | "right": 2993,
1204 | "south": -17.308687886770024,
1205 | "style": "IPY_MODEL_2844db030e5d4cf48dc4184d9d199e01",
1206 | "top": 1848,
1207 | "west": -82.96875000000001,
1208 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/01_ipyleaflet.ipynb",
1209 | "zoom": 4
1210 | }
1211 | },
1212 | "8a55fcdecff644bd895f52388ed1105f": {
1213 | "model_module": "jupyter-leaflet",
1214 | "model_module_version": "^0.17",
1215 | "model_name": "LeafletTileLayerModel",
1216 | "state": {
1217 | "_model_module_version": "^0.17",
1218 | "_view_module_version": "^0.17",
1219 | "name": "Landsat",
1220 | "options": [
1221 | "attribution",
1222 | "bounds",
1223 | "detect_retina",
1224 | "max_native_zoom",
1225 | "max_zoom",
1226 | "min_native_zoom",
1227 | "min_zoom",
1228 | "no_wrap",
1229 | "tile_size",
1230 | "tms",
1231 | "zoom_offset"
1232 | ],
1233 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/6ad5740ea0606cba020bc83b7def6383-8c928b0f3ff829c4c984ad3ff1766542/tiles/{z}/{x}/{y}"
1234 | }
1235 | },
1236 | "8b4f1b315674492aa2aa0304fb09dbe9": {
1237 | "model_module": "@jupyter-widgets/base",
1238 | "model_module_version": "2.0.0",
1239 | "model_name": "LayoutModel",
1240 | "state": {
1241 | "max_height": "400px",
1242 | "overflow": "scroll",
1243 | "width": "100%"
1244 | }
1245 | },
1246 | "8d15fb3843af4c09a8df2e0c75b36237": {
1247 | "model_module": "@jupyter-widgets/controls",
1248 | "model_module_version": "2.0.0",
1249 | "model_name": "SliderStyleModel",
1250 | "state": {
1251 | "description_width": ""
1252 | }
1253 | },
1254 | "91f2a10b7d3b448988309cca16a310d7": {
1255 | "model_module": "jupyter-leaflet",
1256 | "model_module_version": "^0.17",
1257 | "model_name": "LeafletMapStyleModel",
1258 | "state": {
1259 | "_model_module_version": "^0.17",
1260 | "cursor": "crosshair"
1261 | }
1262 | },
1263 | "928f6d23fc54418cab73043865b95bab": {
1264 | "model_module": "@jupyter-widgets/controls",
1265 | "model_module_version": "2.0.0",
1266 | "model_name": "IntSliderModel",
1267 | "state": {
1268 | "behavior": "drag-tap",
1269 | "description": "Zoom level:",
1270 | "layout": "IPY_MODEL_0a0f61d71c324cce8a8724a97449ba20",
1271 | "max": 15,
1272 | "style": "IPY_MODEL_8d15fb3843af4c09a8df2e0c75b36237",
1273 | "value": 7
1274 | }
1275 | },
1276 | "9dc137b26471496981796af08159c660": {
1277 | "model_module": "@jupyter-widgets/base",
1278 | "model_module_version": "2.0.0",
1279 | "model_name": "LayoutModel",
1280 | "state": {
1281 | "max_height": "400px",
1282 | "overflow": "scroll",
1283 | "width": "100%"
1284 | }
1285 | },
1286 | "9e3fd2116ffe4fc5b8faac5584ff6a88": {
1287 | "model_module": "@jupyter-widgets/base",
1288 | "model_module_version": "2.0.0",
1289 | "model_name": "LayoutModel",
1290 | "state": {
1291 | "width": "100%"
1292 | }
1293 | },
1294 | "9f173486cd994f3ab7b1d4451a0a9ce3": {
1295 | "model_module": "ipytree",
1296 | "model_module_version": "^0.2",
1297 | "model_name": "TreeModel",
1298 | "state": {
1299 | "_model_module_version": "^0.2",
1300 | "_view_module_version": "^0.2",
1301 | "layout": "IPY_MODEL_c1a6ad8f1f51414696d19cb2a79f36c9",
1302 | "nodes": [
1303 | "IPY_MODEL_f7a808655c114082b3c5c4c9d8263d89"
1304 | ]
1305 | }
1306 | },
1307 | "a6463125bcf344ffaa11dd7c575fe8f6": {
1308 | "model_module": "ipytree",
1309 | "model_module_version": "^0.2",
1310 | "model_name": "NodeModel",
1311 | "state": {
1312 | "_id": "b067a614-f714-4875-b7bf-3b014808b902",
1313 | "_model_module_version": "^0.2",
1314 | "_view_module_version": "^0.2",
1315 | "name": "string: hello!"
1316 | }
1317 | },
1318 | "aa7270cfa51341e2ba6ac0c04873ca0c": {
1319 | "model_module": "jupyter-leaflet",
1320 | "model_module_version": "^0.17",
1321 | "model_name": "LeafletMapStyleModel",
1322 | "state": {
1323 | "_model_module_version": "^0.17"
1324 | }
1325 | },
1326 | "aa81a18f0da34b6aa80763a9d1de3136": {
1327 | "model_module": "ipytree",
1328 | "model_module_version": "^0.2",
1329 | "model_name": "NodeModel",
1330 | "state": {
1331 | "_id": "19a08d4f-ad31-4145-9cd6-5338c2031d47",
1332 | "_model_module_version": "^0.2",
1333 | "_view_module_version": "^0.2",
1334 | "name": "JSON: [[\"nested\", \"list\"], {\"a nested\": \"dictionary\"}]"
1335 | }
1336 | },
1337 | "ac161b0be8ea49ccb0c2dd62cd51757c": {
1338 | "model_module": "jupyter-leaflet",
1339 | "model_module_version": "^0.17",
1340 | "model_name": "LeafletMapStyleModel",
1341 | "state": {
1342 | "_model_module_version": "^0.17",
1343 | "cursor": "move"
1344 | }
1345 | },
1346 | "b5842c361b8a4929ac798f49ebbf8216": {
1347 | "model_module": "ipytree",
1348 | "model_module_version": "^0.2",
1349 | "model_name": "NodeModel",
1350 | "state": {
1351 | "_id": "46ec3138-89c7-45dc-8893-941421b09f16",
1352 | "_model_module_version": "^0.2",
1353 | "_view_module_version": "^0.2",
1354 | "name": "Click on the map to inspect the layers."
1355 | }
1356 | },
1357 | "b8c74a6b8f8e452b95178758be26c015": {
1358 | "model_module": "@jupyter-widgets/base",
1359 | "model_module_version": "2.0.0",
1360 | "model_name": "LayoutModel",
1361 | "state": {}
1362 | },
1363 | "c0c3c520dd564b9a990b367c80d02320": {
1364 | "model_module": "jupyter-leaflet",
1365 | "model_module_version": "^0.17",
1366 | "model_name": "LeafletLayersControlModel",
1367 | "state": {
1368 | "_model_module_version": "^0.17",
1369 | "_view_module_version": "^0.17",
1370 | "options": [
1371 | "position"
1372 | ],
1373 | "position": "topright"
1374 | }
1375 | },
1376 | "c1a6ad8f1f51414696d19cb2a79f36c9": {
1377 | "model_module": "@jupyter-widgets/base",
1378 | "model_module_version": "2.0.0",
1379 | "model_name": "LayoutModel",
1380 | "state": {
1381 | "max_height": "400px",
1382 | "overflow": "scroll",
1383 | "width": "100%"
1384 | }
1385 | },
1386 | "c6ecac66ad9b4db3b345f2ee7596a468": {
1387 | "model_module": "ipytree",
1388 | "model_module_version": "^0.2",
1389 | "model_name": "NodeModel",
1390 | "state": {
1391 | "_id": "5e228c35-05e9-440b-9a88-6ffb1534ab21",
1392 | "_model_module_version": "^0.2",
1393 | "_view_module_version": "^0.2",
1394 | "name": "nesting: List (2 elements)",
1395 | "nodes": [
1396 | "IPY_MODEL_aa81a18f0da34b6aa80763a9d1de3136"
1397 | ],
1398 | "opened": false
1399 | }
1400 | },
1401 | "c7fb9ecc8e80462e9b9bf33a74800c51": {
1402 | "model_module": "jupyter-leaflet",
1403 | "model_module_version": "^0.17",
1404 | "model_name": "LeafletMapStyleModel",
1405 | "state": {
1406 | "_model_module_version": "^0.17",
1407 | "cursor": "move"
1408 | }
1409 | },
1410 | "cfab475a798448b1916786263b9a8e90": {
1411 | "model_module": "jupyter-leaflet",
1412 | "model_module_version": "^0.17",
1413 | "model_name": "LeafletMapModel",
1414 | "state": {
1415 | "_model_module_version": "^0.17",
1416 | "_view_module_version": "^0.17",
1417 | "bottom": 2248,
1418 | "controls": [
1419 | "IPY_MODEL_2230df77a880469cb030cb1cdd046a6b",
1420 | "IPY_MODEL_2d81e9ccd8c14032b275563f33f194e2",
1421 | "IPY_MODEL_160f1f17eda549e794967ad36033535f"
1422 | ],
1423 | "default_style": "IPY_MODEL_51dc8cde17a84b6897839090ec65fa08",
1424 | "dragging_style": "IPY_MODEL_e2272cc617f34d7aa3d0c60c8b564f96",
1425 | "east": 83.05664062500001,
1426 | "fullscreen": false,
1427 | "interpolation": "bilinear",
1428 | "layers": [
1429 | "IPY_MODEL_33a61cf764cb4927920c8222e90ccb60"
1430 | ],
1431 | "layout": "IPY_MODEL_9e3fd2116ffe4fc5b8faac5584ff6a88",
1432 | "left": 1104,
1433 | "modisdate": "2023-01-10",
1434 | "north": 17.308687886770034,
1435 | "options": [
1436 | "bounce_at_zoom_limits",
1437 | "box_zoom",
1438 | "center",
1439 | "close_popup_on_click",
1440 | "double_click_zoom",
1441 | "dragging",
1442 | "fullscreen",
1443 | "inertia",
1444 | "inertia_deceleration",
1445 | "inertia_max_speed",
1446 | "interpolation",
1447 | "keyboard",
1448 | "keyboard_pan_offset",
1449 | "keyboard_zoom_offset",
1450 | "max_zoom",
1451 | "min_zoom",
1452 | "prefer_canvas",
1453 | "scroll_wheel_zoom",
1454 | "tap",
1455 | "tap_tolerance",
1456 | "touch_zoom",
1457 | "world_copy_jump",
1458 | "zoom",
1459 | "zoom_animation_threshold",
1460 | "zoom_delta",
1461 | "zoom_snap"
1462 | ],
1463 | "prefer_canvas": false,
1464 | "right": 2993,
1465 | "south": -17.308687886770024,
1466 | "style": "IPY_MODEL_6f2ea328676a49879861b6b4bcc561d7",
1467 | "top": 1848,
1468 | "west": -82.96875000000001,
1469 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/01_ipyleaflet.ipynb",
1470 | "zoom": 4
1471 | }
1472 | },
1473 | "d0b665c8f34149b59bab95267946ec4b": {
1474 | "model_module": "@jupyter-widgets/base",
1475 | "model_module_version": "2.0.0",
1476 | "model_name": "LayoutModel",
1477 | "state": {
1478 | "width": "100%"
1479 | }
1480 | },
1481 | "d0d292fd509a42199487515df00981f0": {
1482 | "model_module": "jupyter-leaflet",
1483 | "model_module_version": "^0.17",
1484 | "model_name": "LeafletMapStyleModel",
1485 | "state": {
1486 | "_model_module_version": "^0.17",
1487 | "cursor": "move"
1488 | }
1489 | },
1490 | "d2f995acae1443b6b935acc8a8c400b6": {
1491 | "model_module": "ipytree",
1492 | "model_module_version": "^0.2",
1493 | "model_name": "NodeModel",
1494 | "state": {
1495 | "_id": "add92e92-ffbe-40bf-9e54-c39aad2c2b17",
1496 | "_model_module_version": "^0.2",
1497 | "_view_module_version": "^0.2",
1498 | "name": "JSON: [1, \"item1\"]"
1499 | }
1500 | },
1501 | "d47faa88d79c4959a37169e1e38e9241": {
1502 | "model_module": "ipytree",
1503 | "model_module_version": "^0.2",
1504 | "model_name": "TreeModel",
1505 | "state": {
1506 | "_model_module_version": "^0.2",
1507 | "_view_module_version": "^0.2",
1508 | "layout": "IPY_MODEL_b8c74a6b8f8e452b95178758be26c015",
1509 | "nodes": [
1510 | "IPY_MODEL_47097130e74244829c4536b3a89300b6",
1511 | "IPY_MODEL_07ae7d0268784ae1a137cb50a08767a3",
1512 | "IPY_MODEL_a6463125bcf344ffaa11dd7c575fe8f6",
1513 | "IPY_MODEL_faf90a6ceecc4f77915f9255bf74b49c",
1514 | "IPY_MODEL_67489456c53340f980a0bead87bddeea",
1515 | "IPY_MODEL_c6ecac66ad9b4db3b345f2ee7596a468"
1516 | ]
1517 | }
1518 | },
1519 | "e09fd56d9fdc4a4096bb45a016011000": {
1520 | "model_module": "ipytree",
1521 | "model_module_version": "^0.2",
1522 | "model_name": "TreeModel",
1523 | "state": {
1524 | "_model_module_version": "^0.2",
1525 | "_view_module_version": "^0.2",
1526 | "layout": "IPY_MODEL_9dc137b26471496981796af08159c660",
1527 | "nodes": [
1528 | "IPY_MODEL_5d3303f3faf04d21b9d362a4d4d77d75"
1529 | ]
1530 | }
1531 | },
1532 | "e2272cc617f34d7aa3d0c60c8b564f96": {
1533 | "model_module": "jupyter-leaflet",
1534 | "model_module_version": "^0.17",
1535 | "model_name": "LeafletMapStyleModel",
1536 | "state": {
1537 | "_model_module_version": "^0.17",
1538 | "cursor": "move"
1539 | }
1540 | },
1541 | "e4512b0df41443fda0e94002f8796ed4": {
1542 | "model_module": "jupyter-leaflet",
1543 | "model_module_version": "^0.17",
1544 | "model_name": "LeafletLayersControlModel",
1545 | "state": {
1546 | "_model_module_version": "^0.17",
1547 | "_view_module_version": "^0.17",
1548 | "options": [
1549 | "position"
1550 | ],
1551 | "position": "topright"
1552 | }
1553 | },
1554 | "ee54cabd174f47ae94402d42c8a5c41b": {
1555 | "model_module": "jupyter-leaflet",
1556 | "model_module_version": "^0.17",
1557 | "model_name": "LeafletAttributionControlModel",
1558 | "state": {
1559 | "_model_module_version": "^0.17",
1560 | "_view_module_version": "^0.17",
1561 | "options": [
1562 | "position",
1563 | "prefix"
1564 | ],
1565 | "position": "bottomright",
1566 | "prefix": "ipyleaflet"
1567 | }
1568 | },
1569 | "f0c9cd6fe4a84c5db48cb2e45895a965": {
1570 | "model_module": "jupyter-leaflet",
1571 | "model_module_version": "^0.17",
1572 | "model_name": "LeafletTileLayerModel",
1573 | "state": {
1574 | "_model_module_version": "^0.17",
1575 | "_view_module_version": "^0.17",
1576 | "name": "My layer",
1577 | "options": [
1578 | "attribution",
1579 | "bounds",
1580 | "detect_retina",
1581 | "max_native_zoom",
1582 | "max_zoom",
1583 | "min_native_zoom",
1584 | "min_zoom",
1585 | "no_wrap",
1586 | "tile_size",
1587 | "tms",
1588 | "zoom_offset"
1589 | ],
1590 | "url": "https://earthengine.googleapis.com/v1alpha/projects/earthengine-legacy/maps/c89c7860f777996759fa533ed2e86632-6aa722b7b97fa5b2a6b6e188d7946079/tiles/{z}/{x}/{y}"
1591 | }
1592 | },
1593 | "f25225841c5d4e5ca0c9f57ba5f01c4a": {
1594 | "model_module": "jupyter-leaflet",
1595 | "model_module_version": "^0.17",
1596 | "model_name": "LeafletTileLayerModel",
1597 | "state": {
1598 | "_model_module_version": "^0.17",
1599 | "_view_module_version": "^0.17",
1600 | "attribution": "Map tiles by Stamen Design, CC BY 3.0 — Map data © OpenStreetMap contributors",
1601 | "base": true,
1602 | "max_zoom": 16,
1603 | "min_zoom": 1,
1604 | "name": "Stamen.Watercolor",
1605 | "options": [
1606 | "attribution",
1607 | "bounds",
1608 | "detect_retina",
1609 | "max_native_zoom",
1610 | "max_zoom",
1611 | "min_native_zoom",
1612 | "min_zoom",
1613 | "no_wrap",
1614 | "tile_size",
1615 | "tms",
1616 | "zoom_offset"
1617 | ],
1618 | "url": "https://stamen-tiles-a.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg"
1619 | }
1620 | },
1621 | "f2c6e70ba9084957aa70d6e157e77c0e": {
1622 | "model_module": "ipytree",
1623 | "model_module_version": "^0.2",
1624 | "model_name": "NodeModel",
1625 | "state": {
1626 | "_id": "06abaae4-b026-4c70-9fe7-d068df811456",
1627 | "_model_module_version": "^0.2",
1628 | "_view_module_version": "^0.2",
1629 | "name": "JSON: {\"a key\": \"a value\"}"
1630 | }
1631 | },
1632 | "f335e46a0a3a4446873a52efb6bd3d99": {
1633 | "model_module": "jupyter-leaflet",
1634 | "model_module_version": "^0.17",
1635 | "model_name": "LeafletMapModel",
1636 | "state": {
1637 | "_model_module_version": "^0.17",
1638 | "_view_module_version": "^0.17",
1639 | "bottom": 2248,
1640 | "controls": [
1641 | "IPY_MODEL_54732d9ee511484c86c640b59f9d1f61",
1642 | "IPY_MODEL_34eccbbe46bb4e7582ff83f2167a587d",
1643 | "IPY_MODEL_e4512b0df41443fda0e94002f8796ed4"
1644 | ],
1645 | "default_style": "IPY_MODEL_3cd16605239f401f80016b0709e90fd0",
1646 | "dragging_style": "IPY_MODEL_ac161b0be8ea49ccb0c2dd62cd51757c",
1647 | "east": 41.39648437500001,
1648 | "fullscreen": false,
1649 | "interpolation": "bilinear",
1650 | "layers": [
1651 | "IPY_MODEL_4e33a3e56de54a788e6516a373dd16cb",
1652 | "IPY_MODEL_798b4009a670401eb10bb0cd4a2d5902",
1653 | "IPY_MODEL_8a55fcdecff644bd895f52388ed1105f"
1654 | ],
1655 | "layout": "IPY_MODEL_46ec4ffb94c54ddf9b7a01b8567ef4e5",
1656 | "left": 1577,
1657 | "modisdate": "2023-01-10",
1658 | "north": 17.308687886770034,
1659 | "options": [
1660 | "bounce_at_zoom_limits",
1661 | "box_zoom",
1662 | "center",
1663 | "close_popup_on_click",
1664 | "double_click_zoom",
1665 | "dragging",
1666 | "fullscreen",
1667 | "inertia",
1668 | "inertia_deceleration",
1669 | "inertia_max_speed",
1670 | "interpolation",
1671 | "keyboard",
1672 | "keyboard_pan_offset",
1673 | "keyboard_zoom_offset",
1674 | "max_zoom",
1675 | "min_zoom",
1676 | "prefer_canvas",
1677 | "scroll_wheel_zoom",
1678 | "tap",
1679 | "tap_tolerance",
1680 | "touch_zoom",
1681 | "world_copy_jump",
1682 | "zoom",
1683 | "zoom_animation_threshold",
1684 | "zoom_delta",
1685 | "zoom_snap"
1686 | ],
1687 | "prefer_canvas": false,
1688 | "right": 2519,
1689 | "south": -17.308687886770024,
1690 | "style": "IPY_MODEL_530c702061914f549aa756a040896abb",
1691 | "top": 1848,
1692 | "west": -41.39648437500001,
1693 | "window_url": "http://localhost:8888/lab/workspaces/auto-c/tree/google/earthengine-jupyter/nbs/01_ipyleaflet.ipynb",
1694 | "zoom": 4
1695 | }
1696 | },
1697 | "f7a808655c114082b3c5c4c9d8263d89": {
1698 | "model_module": "ipytree",
1699 | "model_module_version": "^0.2",
1700 | "model_name": "NodeModel",
1701 | "state": {
1702 | "_id": "9612679a-cf1b-4641-bb42-14117f09d52d",
1703 | "_model_module_version": "^0.2",
1704 | "_view_module_version": "^0.2",
1705 | "name": "Click on the map to inspect the layers."
1706 | }
1707 | },
1708 | "faf90a6ceecc4f77915f9255bf74b49c": {
1709 | "model_module": "ipytree",
1710 | "model_module_version": "^0.2",
1711 | "model_name": "NodeModel",
1712 | "state": {
1713 | "_id": "b9848e8a-faf4-4862-8a7c-bc2055cc94bb",
1714 | "_model_module_version": "^0.2",
1715 | "_view_module_version": "^0.2",
1716 | "name": "list: List (2 elements)",
1717 | "nodes": [
1718 | "IPY_MODEL_d2f995acae1443b6b935acc8a8c400b6"
1719 | ],
1720 | "opened": false
1721 | }
1722 | }
1723 | },
1724 | "version_major": 2,
1725 | "version_minor": 0
1726 | }
1727 | }
1728 | },
1729 | "nbformat": 4,
1730 | "nbformat_minor": 5
1731 | }
1732 |
--------------------------------------------------------------------------------