From 086230e8b0a30e6af67678de191f8ae1844ea255 Mon Sep 17 00:00:00 2001 From: Qiusheng Wu Date: Sun, 5 Jul 2020 12:10:38 -0400 Subject: [PATCH] Add support for publishing maps #109 --- README.rst | 8 + examples/notebooks/24_publish_maps.ipynb | 189 +++++++++++++++++++++++ geemap/eefolium.py | 56 ++++++- geemap/geemap.py | 18 ++- 4 files changed, 261 insertions(+), 10 deletions(-) create mode 100644 examples/notebooks/24_publish_maps.ipynb diff --git a/README.rst b/README.rst index 395bb031d3..183a33f234 100644 --- a/README.rst +++ b/README.rst @@ -107,6 +107,7 @@ Below is a partial list of features available for the geemap package. Please che * Exporting Earth Engine maps as HTML files and PNG images. * Searching Earth Engine API documentation within Jupyter notebooks. * Importing Earth Engine assets from personal account. +# Publishing interactive GEE maps directly within Jupyter notebook. Installation @@ -423,6 +424,13 @@ To search Earth Engine API documentation with Jupyter notebooks: geemap.ee_search() +To publish an interactive GEE map with Jupyter notebooks: + +.. code:: python + + Map.publish(name, headline, visibility) + + Examples -------- diff --git a/examples/notebooks/24_publish_maps.ipynb b/examples/notebooks/24_publish_maps.ipynb new file mode 100644 index 0000000000..3e02875732 --- /dev/null +++ b/examples/notebooks/24_publish_maps.ipynb @@ -0,0 +1,189 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To follow this tutorial, you will need to [sign up](https://datapane.com/accounts/signup/) for an account with , then install and authenticate the `datapane` Python package. More information can be found [here](https://docs.datapane.com/tutorials/tut-getting-started). \n", + "\n", + "- `pip install datapane`\n", + "- `datapane login`\n", + "- `datapane ping`" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": {}, + "outputs": [], + "source": [ + "import ee \n", + "import geemap.eefolium as geemap" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "# Create a map centered at (lat, lon).\n", + "Map = geemap.Map(center=[40, -100], zoom=4)\n", + "\n", + "# Use an elevation dataset and terrain functions to create\n", + "# a custom visualization of topography.\n", + "\n", + "# Load a global elevation image.\n", + "elev = ee.Image('USGS/GMTED2010')\n", + "\n", + "# Zoom to an area of interest.\n", + "Map.setCenter(-121.069, 50.709, 6)\n", + "\n", + "# Add the elevation to the map.\n", + "Map.addLayer(elev, {}, 'elev')\n", + "\n", + "# Use the terrain algorithms to compute a hillshade with 8-bit values.\n", + "shade = ee.Terrain.hillshade(elev)\n", + "Map.addLayer(shade, {}, 'hillshade', False)\n", + "\n", + "# Create a \"sea\" variable to be used for cartographic purposes\n", + "sea = elev.lte(0)\n", + "Map.addLayer(sea.mask(sea), {'palette':'000022'}, 'sea', False)\n", + "\n", + "# Create a custom elevation palette from hex strings.\n", + "elevationPalette = ['006600', '002200', 'fff700', 'ab7634', 'c4d0ff', 'ffffff']\n", + "# Use these visualization parameters, customized by location.\n", + "visParams = {'min': 1, 'max': 3000, 'palette': elevationPalette}\n", + "\n", + "# Create a mosaic of the sea and the elevation data\n", + "visualized = ee.ImageCollection([\n", + " # Mask the elevation to get only land\n", + " elev.mask(sea.Not()).visualize(**visParams),\n", + " # Use the sea mask directly to display sea.\n", + " sea.mask(sea).visualize(**{'palette':'000022'})\n", + "]).mosaic()\n", + "\n", + "# Note that the visualization image doesn't require visualization parameters.\n", + "Map.addLayer(visualized, {}, 'elev palette', False)\n", + "\n", + "# Convert the visualized elevation to HSV, first converting to [0, 1] data.\n", + "hsv = visualized.divide(255).rgbToHsv()\n", + "# Select only the hue and saturation bands.\n", + "hs = hsv.select(0, 1)\n", + "# Convert the hillshade to [0, 1] data, as expected by the HSV algorithm.\n", + "v = shade.divide(255)\n", + "# Create a visualization image by converting back to RGB from HSV.\n", + "# Note the cast to byte in order to export the image correctly.\n", + "rgb = hs.addBands(v).hsvToRgb().multiply(255).byte()\n", + "Map.addLayer(rgb, {}, 'styled')\n", + "\n", + "states = ee.FeatureCollection('TIGER/2018/States')\n", + "Map.addLayer(ee.Image().paint(states, 0, 2), {}, \"US States\")" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Add layer control to the map.\n", + "Map.setControlVisibility()\n", + "# Display the map.\n", + "Map" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "metadata": {}, + "outputs": [], + "source": [ + "Map.publish(name='gee_folium_map', headline='Terrain Visualization', visibility='PUBLIC', overwrite=True)" + ] + } + ], + "metadata": { + "hide_input": false, + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.2" + }, + "toc": { + "base_numbering": 1, + "nav_menu": {}, + "number_sections": true, + "sideBar": true, + "skip_h1_title": true, + "title_cell": "Table of Contents", + "title_sidebar": "Table of Contents", + "toc_cell": false, + "toc_position": { + "height": "calc(100% - 180px)", + "left": "10px", + "top": "150px", + "width": "318px" + }, + "toc_section_display": true, + "toc_window_display": true + }, + "varInspector": { + "cols": { + "lenName": 16, + "lenType": 16, + "lenVar": 40 + }, + "kernels_config": { + "python": { + "delete_cmd_postfix": "", + "delete_cmd_prefix": "del ", + "library": "var_list.py", + "varRefreshCmd": "print(var_dic_list())" + }, + "r": { + "delete_cmd_postfix": ") ", + "delete_cmd_prefix": "rm(", + "library": "var_list.r", + "varRefreshCmd": "cat(var_dic_list()) " + } + }, + "types_to_exclude": [ + "module", + "function", + "builtin_function_or_method", + "instance", + "_Feature" + ], + "window_display": false + } + }, + "nbformat": 4, + "nbformat_minor": 4 +} diff --git a/geemap/eefolium.py b/geemap/eefolium.py index c1b580d3ed..cf0f7b9cee 100644 --- a/geemap/eefolium.py +++ b/geemap/eefolium.py @@ -176,7 +176,8 @@ class Map(folium.Map): def __init__(self, **kwargs): import logging - logging.getLogger('googleapiclient.discovery_cache').setLevel(logging.ERROR) + logging.getLogger( + 'googleapiclient.discovery_cache').setLevel(logging.ERROR) ee_initialize() # Default map center location and zoom level @@ -416,20 +417,23 @@ def add_tile_layer(self, tiles='OpenStreetMap', name=None, attribution='', overl except: print("Failed to add the specified TileLayer.") - def publish(self, name=None, headline='Untitled', visibility='PUBLIC', open=True): + def publish(self, name=None, headline='Untitled', visibility='PUBLIC', overwrite=True, open=True): """Publish the map to datapane.com Args: name (str, optional): The URL of the map. Defaults to None. headline (str, optional): Title of the map. Defaults to 'Untitled'. visibility (str, optional): Visibility of the map. It can be one of the following: PUBLIC, PRIVATE, ORG. Defaults to 'PUBLIC'. + overwrite (bool, optional): Whether to overwrite the existing map with the same name. Defaults to True. open (bool, optional): Whether to open the map. Defaults to True. """ + import webbrowser try: import datapane as dp except Exception as e: - print('The datapane package is not installed.') - print(e) + print('The datapane Python package is not installed. You need to install and authenticate datapane first.') + webbrowser.open_new_tab( + 'https://docs.datapane.com/tutorials/tut-getting-started') return import datapane as dp @@ -443,11 +447,53 @@ def publish(self, name=None, headline='Untitled', visibility='PUBLIC', open=True if visibility not in ['PUBLIC', 'PRIVATE', 'ORG']: visibility = 'PRIVATE' + if overwrite: + delete_dp_report(name) + report = dp.Report(dp.Plot(self)) report.publish(name=name, headline=headline, visibility=visibility, open=open) +def delete_dp_report(name): + """Deletes a datapane report. + + Args: + name (str): Name of the report to delete. + """ + try: + import datapane as dp + + reports = dp.Report.list() + items = list(reports) + names = list(map(lambda item: item['name'], items)) + if name in names: + report = dp.Report.get(name) + url = report.blocks[0]['url'] + # print('Deleting {}...'.format(url)) + dp.Report.delete(dp.Report.by_id(url)) + except Exception as e: + print(e) + return + + +def delete_dp_reports(): + """Deletes all datapane reports. + """ + try: + import datapane as dp + reports = dp.Report.list() + for item in reports: + print(item['name']) + report = dp.Report.get(item['name']) + url = report.blocks[0]['url'] + print('Deleting {}...'.format(url)) + dp.Report.delete(dp.Report.by_id(url)) + except Exception as e: + print(e) + return + + def install_from_github(url): """Install a package from a GitHub repository. @@ -1168,7 +1214,7 @@ def check_install(package): try: __import__(package) - print('{} is already installed.'.format(package)) + # print('{} is already installed.'.format(package)) except ImportError: print('{} is not installed. Installing ...'.format(package)) try: diff --git a/geemap/geemap.py b/geemap/geemap.py index f91835169b..89ff74d5d9 100644 --- a/geemap/geemap.py +++ b/geemap/geemap.py @@ -115,9 +115,9 @@ def __init__(self, **kwargs): search_button = widgets.ToggleButton( value=False, tooltip='Search location/data', - icon='search' + icon='globe' ) - search_button.layout.width = '37px' + search_button.layout.width = '36px' search_type = widgets.ToggleButtons( options=['name/address', 'lat-lon', 'data'], @@ -305,10 +305,19 @@ def search_box_callback(text): search_widget = widgets.HBox() search_widget.children = [search_button] - search_control = WidgetControl( + data_control = WidgetControl( widget=search_widget, position='topleft') - self.add_control(control=search_control) + self.add_control(control=data_control) + + search_marker = Marker(icon=AwesomeIcon(name="check", marker_color='green', icon_color='darkgreen')) + search = SearchControl(position="topleft", + url='https://nominatim.openstreetmap.org/search?format=json&q={s}', + zoom=5, + property_name='display_name', + marker=search_marker + ) + self.add_control(search) self.add_control(ZoomControl(position='topleft')) @@ -3717,7 +3726,6 @@ def sentinel2_timeseries(roi=None, start_year=2015, end_year=2019, start_date='0 Returns: object: Returns an ImageCollection containing annual Sentinel 2 images. """ - ################################################################################ ################################################################################