diff --git a/install.yml b/install.yml index e36a6a3..ffbaf82 100644 --- a/install.yml +++ b/install.yml @@ -1,5 +1,7 @@ # This file should be committed to your app code. version: 1.0 +# This should be greater or equal to your tethys-platform in your environment +tethys_version: ">=4.0.0" # This should match the app - package name in your setup.py name: earth_engine @@ -16,4 +18,6 @@ requirements: - pyshp pip: -post: + npm: + +post: \ No newline at end of file diff --git a/setup.py b/setup.py index 90a817f..7f32563 100644 --- a/setup.py +++ b/setup.py @@ -1,16 +1,16 @@ from setuptools import setup, find_namespace_packages -from tethys_apps.app_installation import find_resource_files +from tethys_apps.app_installation import find_all_resource_files +from tethys_apps.base.app_base import TethysAppBase # -- Apps Definition -- # app_package = 'earth_engine' -release_package = 'tethysapp-' + app_package +release_package = f'{TethysAppBase.package_namespace}-{app_package}' # -- Python Dependencies -- # dependencies = [] # -- Get Resource File -- # -resource_files = find_resource_files('tethysapp/' + app_package + '/templates', 'tethysapp/' + app_package) -resource_files += find_resource_files('tethysapp/' + app_package + '/public', 'tethysapp/' + app_package) +resource_files = find_all_resource_files(app_package, TethysAppBase.package_namespace) setup( diff --git a/tethysapp/earth_engine/__init__.py b/tethysapp/earth_engine/__init__.py index 8766881..c927d02 100644 --- a/tethysapp/earth_engine/__init__.py +++ b/tethysapp/earth_engine/__init__.py @@ -1 +1 @@ -# Included for native namespace package support \ No newline at end of file +# Included for native namespace package support diff --git a/tethysapp/earth_engine/app.py b/tethysapp/earth_engine/app.py index 23889dc..d368756 100644 --- a/tethysapp/earth_engine/app.py +++ b/tethysapp/earth_engine/app.py @@ -1,4 +1,4 @@ -from tethys_sdk.base import TethysAppBase, url_map_maker +from tethys_sdk.base import TethysAppBase class EarthEngine(TethysAppBase): @@ -6,49 +6,13 @@ class EarthEngine(TethysAppBase): Tethys app class for Earth Engine. """ - name = 'Earth Engine' - index = 'earth_engine:home' - icon = 'earth_engine/images/earth-engine-logo.png' - package = 'earth_engine' + name = 'Google Earth Engine Tutorial' + description = '' + package = 'earth_engine' # WARNING: Do not change this value + index = 'home' + icon = f'{package}/images/earth-engine-logo.png' root_url = 'earth-engine' color = '#524745' - description = '' tags = '' enable_feedback = False - feedback_emails = [] - - def url_maps(self): - """ - Add controllers - """ - UrlMap = url_map_maker(self.root_url) - - url_maps = ( - UrlMap( - name='home', - url='earth-engine', - controller='earth_engine.controllers.home' - ), - UrlMap( - name='viewer', - url='earth-engine/viewer', - controller='earth_engine.controllers.viewer' - ), - UrlMap( - name='get_image_collection', - url='earth-engine/viewer/get-image-collection', - controller='earth_engine.controllers.get_image_collection' - ), - UrlMap( - name='get_time_series_plot', - url='earth-engine/viewer/get-time-series-plot', - controller='earth_engine.controllers.get_time_series_plot' - ), - UrlMap( - name='about', - url='earth-engine/about', - controller='earth_engine.controllers.about' - ) - ) - - return url_maps + feedback_emails = [] \ No newline at end of file diff --git a/tethysapp/earth_engine/controllers.py b/tethysapp/earth_engine/controllers.py index 4bbef10..f87b940 100644 --- a/tethysapp/earth_engine/controllers.py +++ b/tethysapp/earth_engine/controllers.py @@ -1,26 +1,20 @@ -import os -import tempfile -import zipfile -import logging import datetime as dt import geojson -import ee -import shapefile +import logging +from simplejson.errors import JSONDecodeError from django.http import JsonResponse, HttpResponseNotAllowed, HttpResponseRedirect from django.shortcuts import render -from simplejson.errors import JSONDecodeError +from tethys_sdk.routing import controller from tethys_sdk.gizmos import SelectInput, DatePicker, Button, MapView, MVView, PlotlyView, MVDraw -from tethys_sdk.permissions import login_required -from tethys_sdk.workspaces import user_workspace -from .helpers import generate_figure, find_shapefile, write_boundary_shapefile, prep_boundary_dir -from .gee.methods import get_image_collection_asset, get_time_series_from_image_collection, upload_shapefile_to_gee, \ +from .gee.methods import get_image_collection_asset, get_time_series_from_image_collection, \ get_boundary_fc_props_for_user from .gee.products import EE_PRODUCTS +from .helpers import generate_figure, handle_shapefile_upload log = logging.getLogger(f'tethys.apps.{__name__}') -@login_required() +@controller def home(request): """ Controller for the app home page. @@ -29,7 +23,7 @@ def home(request): return render(request, 'earth_engine/home.html', context) -@login_required() +@controller def about(request): """ Controller for the app about page. @@ -38,8 +32,7 @@ def about(request): return render(request, 'earth_engine/about.html', context) -@login_required() -@user_workspace +@controller(user_workspace=True) def viewer(request, user_workspace): """ Controller for the app viewer page. @@ -146,24 +139,10 @@ def viewer(request, user_workspace): load_button = Button( name='load_map', display_text='Load', - style='default', + style='outline-secondary', attributes={'id': 'load_map'} ) - - clear_button = Button( - name='clear_map', - display_text='Clear', - style='default', - attributes={'id': 'clear_map'} - ) - - plot_button = Button( - name='load_plot', - display_text='Plot AOI', - style='default', - attributes={'id': 'load_plot'} - ) - + # Get bounding box from user boundary if it exists boundary_props = get_boundary_fc_props_for_user(request.user) @@ -198,15 +177,30 @@ def viewer(request, user_workspace): ) ) + clear_button = Button( + name='clear_map', + display_text='Clear', + style='outline-secondary', + attributes={'id': 'clear_map'}, + classes='mt-2', + ) + + plot_button = Button( + name='load_plot', + display_text='Plot AOI', + style='outline-secondary', + attributes={'id': 'load_plot'}, + ) + # Boundary Upload Form set_boundary_button = Button( name='set_boundary', display_text='Set Boundary', - style='default', + style='outline-secondary', attributes={ 'id': 'set_boundary', - 'data-toggle': 'modal', - 'data-target': '#set-boundary-modal' # ID of the Set Boundary Modal + 'data-bs-toggle': 'modal', + 'data-bs-target': '#set-boundary-modal', # ID of the Set Boundary Modal } ) @@ -238,7 +232,7 @@ def viewer(request, user_workspace): return render(request, 'earth_engine/viewer.html', context) -@login_required() +@controller(url='viewer/get-image-collection') def get_image_collection(request): """ Controller to handle image collection requests. @@ -280,8 +274,7 @@ def get_image_collection(request): return JsonResponse(response_data) - -@login_required() +@controller(url='viewer/get-time-series-plot') def get_time_series_plot(request): context = {'success': False} @@ -351,65 +344,3 @@ def get_time_series_plot(request): log.exception('An unexpected error occurred.') return render(request, 'earth_engine/plot.html', context) - - -def handle_shapefile_upload(request, user_workspace): - """ - Uploads shapefile to Google Earth Engine as an Asset. - - Args: - request (django.Request): the request object. - user_workspace (tethys_sdk.workspaces.Workspace): the User workspace object. - - Returns: - str: Error string if errors occurred. - """ - # Write file to temp for processing - uploaded_file = request.FILES['boundary-file'] - - with tempfile.TemporaryDirectory() as temp_dir: - temp_zip_path = os.path.join(temp_dir, 'boundary.zip') - - # Use with statements to ensure opened files are closed when done - with open(temp_zip_path, 'wb') as temp_zip: - for chunk in uploaded_file.chunks(): - temp_zip.write(chunk) - - try: - # Extract the archive to the temporary directory - with zipfile.ZipFile(temp_zip_path) as temp_zip: - temp_zip.extractall(temp_dir) - - except zipfile.BadZipFile: - # Return error message - return 'You must provide a zip archive containing a shapefile.' - - # Verify that it contains a shapefile - try: - # Find a shapefile in directory where we extracted the archive - shapefile_path = find_shapefile(temp_dir) - - if not shapefile_path: - return 'No Shapefile found in the archive provided.' - - with shapefile.Reader(shapefile_path) as shp_file: - # Check type (only Polygon supported) - if shp_file.shapeType != shapefile.POLYGON: - return 'Only shapefiles containing Polygons are supported.' - - # Setup workspace directory for storing shapefile - workspace_dir = prep_boundary_dir(user_workspace.path) - - # Write the shapefile to the workspace directory - write_boundary_shapefile(shp_file, workspace_dir) - - # Upload shapefile as Asset in GEE - upload_shapefile_to_gee(request.user, shp_file) - - except TypeError: - return 'Incomplete or corrupted shapefile provided.' - - except ee.EEException: - msg = 'An unexpected error occurred while uploading the shapefile to Google Earth Engine.' - log.exception(msg) - return msg diff --git a/tethysapp/earth_engine/gee/cloud_mask.py b/tethysapp/earth_engine/gee/cloud_mask.py index ac99943..6ffc3bf 100644 --- a/tethysapp/earth_engine/gee/cloud_mask.py +++ b/tethysapp/earth_engine/gee/cloud_mask.py @@ -3,7 +3,7 @@ def mask_l8_sr(image): """ - Derived From: https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LC08_C01_T1_SR + Cloud Mask for Landsat 8 surface reflectance. Derived From: https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LC08_C01_T1_SR """ # Bits 3 and 5 are cloud shadow and cloud, respectively. cloudShadowBitMask = (1 << 3) @@ -19,7 +19,7 @@ def mask_l8_sr(image): def cloud_mask_l457(image): """ - Derived From: https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LE07_C01_T1_SR + Cloud Mask for Landsat 7 surface reflectance. Derived From: https://developers.google.com/earth-engine/datasets/catalog/LANDSAT_LE07_C01_T1_SR """ qa = image.select('pixel_qa') @@ -35,7 +35,7 @@ def cloud_mask_l457(image): def mask_s2_clouds(image): """ - Derived from: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2 + Cloud Mask for Sentinel 2 surface reflectance. Derived from: https://developers.google.com/earth-engine/datasets/catalog/COPERNICUS_S2 """ qa = image.select('QA60') diff --git a/tethysapp/earth_engine/gee/methods.py b/tethysapp/earth_engine/gee/methods.py index a9a8ac6..51eb1cc 100644 --- a/tethysapp/earth_engine/gee/methods.py +++ b/tethysapp/earth_engine/gee/methods.py @@ -1,14 +1,13 @@ -import os import math +import os import logging import ee from ee.ee_exception import EEException import geojson import pandas as pd +from .products import EE_PRODUCTS from . import params as gee_account from . import cloud_mask as cm -from .products import EE_PRODUCTS - log = logging.getLogger(f'tethys.apps.{__name__}') diff --git a/tethysapp/earth_engine/handoff.py b/tethysapp/earth_engine/handoff.py deleted file mode 100644 index 1e15fb8..0000000 --- a/tethysapp/earth_engine/handoff.py +++ /dev/null @@ -1,3 +0,0 @@ -# Define your handoff handlers here -# for more information, see: -# http://docs.tethysplatform.org/en/dev/tethys_sdk/handoff.html \ No newline at end of file diff --git a/tethysapp/earth_engine/helpers.py b/tethysapp/earth_engine/helpers.py index 691a07f..6aa96cb 100644 --- a/tethysapp/earth_engine/helpers.py +++ b/tethysapp/earth_engine/helpers.py @@ -1,8 +1,15 @@ import os +import logging import glob -import shapefile +import tempfile +import zipfile +import ee import pandas as pd from plotly import graph_objs as go +import shapefile +from .gee.methods import upload_shapefile_to_gee + +log = logging.getLogger(f'tethys.apps.{__name__}') def generate_figure(figure_title, time_series): @@ -54,6 +61,68 @@ def generate_figure(figure_title, time_series): return figure +def handle_shapefile_upload(request, user_workspace): + """ + Uploads shapefile to Google Earth Engine as an Asset. + + Args: + request (django.Request): the request object. + user_workspace (tethys_sdk.workspaces.Workspace): the User workspace object. + + Returns: + str: Error string if errors occurred. + """ + # Write file to temp for processing + uploaded_file = request.FILES['boundary-file'] + + with tempfile.TemporaryDirectory() as temp_dir: + temp_zip_path = os.path.join(temp_dir, 'boundary.zip') + + # Use with statements to ensure opened files are closed when done + with open(temp_zip_path, 'wb') as temp_zip: + for chunk in uploaded_file.chunks(): + temp_zip.write(chunk) + + try: + # Extract the archive to the temporary directory + with zipfile.ZipFile(temp_zip_path) as temp_zip: + temp_zip.extractall(temp_dir) + + except zipfile.BadZipFile: + # Return error message + return 'You must provide a zip archive containing a shapefile.' + + # Verify that it contains a shapefile + try: + # Find a shapefile in directory where we extracted the archive + shapefile_path = find_shapefile(temp_dir) + + if not shapefile_path: + return 'No Shapefile found in the archive provided.' + + with shapefile.Reader(shapefile_path) as shp_file: + # Check type (only Polygon supported) + if shp_file.shapeType != shapefile.POLYGON: + return 'Only shapefiles containing Polygons are supported.' + + # Setup workspace directory for storing shapefile + workspace_dir = prep_boundary_dir(user_workspace.path) + + # Write the shapefile to the workspace directory + write_boundary_shapefile(shp_file, workspace_dir) + + # Upload shapefile as Asset in GEE + upload_shapefile_to_gee(request.user, shp_file) + + except TypeError: + return 'Incomplete or corrupted shapefile provided.' + + except ee.EEException: + msg = 'An unexpected error occurred while uploading the shapefile to Google Earth Engine.' + log.exception(msg) + return msg + + def find_shapefile(directory): """ Recursively find the path to the first file with an extension ".shp" in the given directory. diff --git a/tethysapp/earth_engine/public/css/disclaimer_modal.css b/tethysapp/earth_engine/public/css/disclaimer_modal.css deleted file mode 100644 index f5d3ff7..0000000 --- a/tethysapp/earth_engine/public/css/disclaimer_modal.css +++ /dev/null @@ -1,20 +0,0 @@ -#disclaimer-container { - height: 400px; - overflow-y: auto; -} - -#sponsors-container { - text-align: left; -} - -#sponsors-container img { - height: 50px; - width: 50px; - margin-right: 10px; - border-radius: 5px; -} - -#sponsors-container h6 { - display: inline-block; - margin-right: 10px; -} diff --git a/tethysapp/earth_engine/public/css/home.css b/tethysapp/earth_engine/public/css/home.css index f0cd423..bde0dd0 100644 --- a/tethysapp/earth_engine/public/css/home.css +++ b/tethysapp/earth_engine/public/css/home.css @@ -1,3 +1,7 @@ +#inner-app-content { + padding: 0; +} + .info-container { background-color: #0000009f; box-shadow: 3px 5px 3px rgba(0,0,0,0.35); @@ -69,4 +73,4 @@ background-color: #d2dadc; background-position: center; background-repeat: no-repeat -} +} \ No newline at end of file diff --git a/tethysapp/earth_engine/public/css/loader.css b/tethysapp/earth_engine/public/css/loader.css index d23c836..b1f287e 100644 --- a/tethysapp/earth_engine/public/css/loader.css +++ b/tethysapp/earth_engine/public/css/loader.css @@ -12,4 +12,4 @@ #loader.show { display: block; -} +} \ No newline at end of file diff --git a/tethysapp/earth_engine/public/css/main.css b/tethysapp/earth_engine/public/css/main.css index b2b732d..be9379d 100644 --- a/tethysapp/earth_engine/public/css/main.css +++ b/tethysapp/earth_engine/public/css/main.css @@ -1,4 +1,4 @@ /* Remove padding on bottom where app-actions section used to be */ #app-content-wrapper #app-content { padding-bottom: 0; -} +} \ No newline at end of file diff --git a/tethysapp/earth_engine/public/css/map.css b/tethysapp/earth_engine/public/css/map.css index 6d72cbb..7a0907e 100644 --- a/tethysapp/earth_engine/public/css/map.css +++ b/tethysapp/earth_engine/public/css/map.css @@ -6,4 +6,4 @@ #inner-app-content { height: 100%; padding: 0; -} +} \ No newline at end of file diff --git a/tethysapp/earth_engine/public/css/no_nav.css b/tethysapp/earth_engine/public/css/no_nav.css deleted file mode 100644 index f114ed9..0000000 --- a/tethysapp/earth_engine/public/css/no_nav.css +++ /dev/null @@ -1,16 +0,0 @@ -#nav-title-wrapper { - margin-left: 15px; -} - -#app-content-wrapper #app-content { - height: 100%; -} - -#app-content-wrapper.show-nav #app-content { - padding-right: 0; - transform: none; -} - -#inner-app-content { - padding: 0; -} diff --git a/tethysapp/earth_engine/public/css/plot.css b/tethysapp/earth_engine/public/css/plot.css index 0a02db7..a8d9f45 100644 --- a/tethysapp/earth_engine/public/css/plot.css +++ b/tethysapp/earth_engine/public/css/plot.css @@ -1,5 +1,9 @@ #plot-loader { - margin: 65px 84px; + display: flex; + align-items: center; + width: 100%; + justify-content: center; + flex-direction: column; } #plot-loader p { @@ -9,3 +13,8 @@ #plot-modal .modal-body { min-height: 480px; } + +.modal-dialog { + max-width: 70vw; + margin: 1.75rem auto; +} \ No newline at end of file diff --git a/tethysapp/earth_engine/public/images/icon.gif b/tethysapp/earth_engine/public/images/icon.gif deleted file mode 100644 index 5c8236e..0000000 Binary files a/tethysapp/earth_engine/public/images/icon.gif and /dev/null differ diff --git a/tethysapp/earth_engine/public/js/gee_datasets.js b/tethysapp/earth_engine/public/js/gee_datasets.js index 68d97bf..ff11280 100644 --- a/tethysapp/earth_engine/public/js/gee_datasets.js +++ b/tethysapp/earth_engine/public/js/gee_datasets.js @@ -76,7 +76,6 @@ var GEE_DATASETS = (function() { } }); - $('#start_date').on('change', function() { let start_date = $('#start_date').val(); @@ -121,10 +120,10 @@ var GEE_DATASETS = (function() { if (!m_platform in EE_PRODUCTS) { alert('Unknown platform selected.'); } - + // Clear sensor options $('#sensor').select2().empty(); - + // Set the Sensor Options let first_option = true; for (var sensor in EE_PRODUCTS[m_platform]) { @@ -133,7 +132,7 @@ var GEE_DATASETS = (function() { $('#sensor').append(new_option); first_option = false; } - + // Trigger a sensor change event to update select box $('#sensor').trigger('change'); update_date_bounds(); @@ -143,12 +142,12 @@ var GEE_DATASETS = (function() { if (!m_platform in EE_PRODUCTS || !m_sensor in EE_PRODUCTS[m_platform]) { alert('Unknown platform or sensor selected.'); } - + // Clear product options $('#product').select2().empty(); - + let first_option = true; - + // Set the Product Options for (var product in EE_PRODUCTS[m_platform][m_sensor]) { let product_display_name = EE_PRODUCTS[m_platform][m_sensor][product]['display']; @@ -156,7 +155,7 @@ var GEE_DATASETS = (function() { $('#product').append(new_option); first_option = false; } - + // Trigger a product change event to update select box $('#product').trigger('change'); update_date_bounds(); @@ -166,24 +165,24 @@ var GEE_DATASETS = (function() { // Get new date picker bounds for the current product let earliest_valid_date = EE_PRODUCTS[m_platform][m_sensor][m_product]['start_date']; let latest_valid_date = EE_PRODUCTS[m_platform][m_sensor][m_product]['end_date']; - + // Get current values of date pickers let current_start_date = $('#start_date').val(); let current_end_date = $('#end_date').val(); - + // Convert to Dates objects for comparison let date_evd = Date.parse(earliest_valid_date); let date_lvd = Date.parse(latest_valid_date) ? (latest_valid_date) : Date.now(); let date_csd = Date.parse(current_start_date); let date_ced = Date.parse(current_end_date); - + // Don't reset currently selected dates if they fall within the new date range let reset_current_dates = true; - + if (date_csd >= date_evd && date_csd <= date_lvd && date_ced >= date_evd && date_ced <= date_lvd) { reset_current_dates = false; } - + // Update start date datepicker bounds $('#start_date').datepicker('setStartDate', earliest_valid_date); $('#start_date').datepicker('setEndDate', latest_valid_date); @@ -191,7 +190,7 @@ var GEE_DATASETS = (function() { $('#start_date').datepicker('update', INITIAL_START_DATE); m_start_date = INITIAL_START_DATE; } - + // Update end date datepicker bounds $('#end_date').datepicker('setStartDate', earliest_valid_date); $('#end_date').datepicker('setEndDate', latest_valid_date); @@ -199,7 +198,7 @@ var GEE_DATASETS = (function() { $('#end_date').datepicker('update', INITIAL_END_DATE); m_end_date = INITIAL_END_DATE; } - + console.log('Date Bounds Changed To: ' + earliest_valid_date + ' - ' + latest_valid_date); }; @@ -219,14 +218,14 @@ var GEE_DATASETS = (function() { // Map Methods update_map = function() { let data = collect_data(); - + let xhr = $.ajax({ type: 'POST', url: 'get-image-collection/', dataType: 'json', data: data }); - + xhr.done(function(response) { if (response.success) { console.log(response.url); @@ -250,25 +249,25 @@ var GEE_DATASETS = (function() { url: url, attributions: 'Google Earth Engine' }); - + source.on('tileloadstart', function() { $('#loader').addClass('show'); }); - + source.on('tileloadend', function() { $('#loader').removeClass('show'); }); - + source.on('tileloaderror', function() { $('#loader').removeClass('show'); }); - + m_gee_layer = new ol.layer.Tile({ source: source, opacity: 0.7 }); - - // Insert below the draw layer (so drawn polygons and points render on top of data layer). + + // Insert below the draw layer (so drawn polygons and points render on top of the data layer). m_map.getLayers().insertAt(1, m_gee_layer); }; @@ -288,9 +287,9 @@ var GEE_DATASETS = (function() { update_plot = function() { let data = collect_data(); - + show_plot_modal(); - + $('#plot-container').load('get-time-series-plot/', data); }; @@ -309,7 +308,7 @@ var GEE_DATASETS = (function() { *************************************************************************/ public_interface = {}; - /************************************************************************ + /************************************************************************ * INITIALIZATION / CONSTRUCTOR *************************************************************************/ $(function() { @@ -330,11 +329,13 @@ var GEE_DATASETS = (function() { m_map = TETHYS_MAP_VIEW.getMap(); // Open boundary file modal if it has an error - if ($('#boundary-file-form-group').hasClass('has-error')) { - $('#set-boundary-modal').modal('show'); + if ($('#boundary-file').hasClass('is-invalid')) { + let boundary_modal_elem = document.getElementById('set-boundary-modal'); + let boundary_modal_inst = bootstrap.Modal.getOrCreateInstance(boundary_modal_elem); + boundary_modal_inst.show(); } }); return public_interface; -}()); // End of package wrapper +}()); // End of package wrapper \ No newline at end of file diff --git a/tethysapp/earth_engine/public/js/main.js b/tethysapp/earth_engine/public/js/main.js index 510998b..b0f978c 100644 --- a/tethysapp/earth_engine/public/js/main.js +++ b/tethysapp/earth_engine/public/js/main.js @@ -30,4 +30,4 @@ $(function() { } } }); -}); //document ready; +}); //document ready; \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/about.html b/tethysapp/earth_engine/templates/earth_engine/about.html index 41c383a..b2390bf 100644 --- a/tethysapp/earth_engine/templates/earth_engine/about.html +++ b/tethysapp/earth_engine/templates/earth_engine/about.html @@ -1,13 +1,13 @@ -{% extends "earth_engine/base.html" %} +{% extends "tethys_apps/app_header_content.html" %} {% load static %} {% block styles %} {{ block.super }} - {% endblock %} -{% block app_navigation_override %} +{% block header_buttons %} + {% include "earth_engine/header_buttons.html" %} {% endblock %} {% block app_content %} @@ -15,43 +15,48 @@ -
- -
-
-

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Euismod nisi porta lorem mollis. Congue quisque egestas diam in arcu cursus euismod. Auctor neque vitae tempus quam pellentesque nec nam. Erat imperdiet sed euismod nisi porta lorem. Nunc eget lorem dolor sed viverra ipsum nunc aliquet bibendum. Sed blandit libero volutpat sed cras ornare. Convallis tellus id interdum velit laoreet id. Amet mauris commodo quis imperdiet massa tincidunt. Mi bibendum neque egestas congue quisque egestas diam in. Enim nec dui nunc mattis enim ut tellus elementum sagittis. Cursus mattis molestie a iaculis at erat pellentesque. Ut tellus elementum sagittis vitae et leo.

-
Eu Consequat ac Felis
-

Eu consequat ac felis donec et odio. Eget arcu dictum varius duis at consectetur lorem. Lorem ipsum dolor sit amet consectetur. Turpis egestas integer eget aliquet nibh praesent. Mattis rhoncus urna neque viverra justo nec. Iaculis urna id volutpat lacus laoreet non curabitur gravida arcu. Convallis posuere morbi leo urna molestie at elementum eu. Fermentum et sollicitudin ac orci phasellus egestas tellus. Convallis aenean et tortor at risus. Morbi tristique senectus et netus et malesuada fames ac. Sed vulputate mi sit amet mauris commodo quis. Nisi quis eleifend quam adipiscing vitae proin sagittis nisl. Id venenatis a condimentum vitae sapien pellentesque habitant morbi tristique. Id cursus metus aliquam eleifend mi in nulla. Proin fermentum leo vel orci porta non pulvinar neque laoreet. Lobortis mattis aliquam faucibus purus in massa tempor. Varius vel pharetra vel turpis nunc.

-

Mauris rhoncus aenean vel elit: Blandit aliquam etiam erat velit. Auctor neque vitae tempus quam pellentesque nec nam. Augue mauris augue neque gravida in fermentum et. Tempus urna et pharetra pharetra. Vel turpis nunc eget lorem. Vitae nunc sed velit dignissim. Enim tortor at auctor urna nunc id. Pellentesque habitant morbi tristique senectus et netus et. Tellus integer feugiat scelerisque varius morbi enim nunc faucibus.

-

Blandit turpis cursus in hac habitasse platea: Tellus elementum sagittis vitae et leo duis ut diam quam. Amet nisl purus in mollis nunc sed. Ac feugiat sed lectus vestibulum. Suscipit adipiscing bibendum est ultricies integer quis. Tortor pretium viverra suspendisse potenti nullam ac tortor. Blandit turpis cursus in hac. Id porta nibh venenatis cras sed felis eget velit. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at. Sapien et ligula ullamcorper malesuada proin libero nunc consequat. Aliquet enim tortor at auctor urna nunc id. Fringilla ut morbi tincidunt augue interdum velit euismod in. In arcu cursus euismod quis viverra nibh. Vulputate ut pharetra sit amet. Purus in massa tempor nec. Pellentesque massa placerat duis ultricies lacus sed. Integer feugiat scelerisque varius morbi enim. Vitae tempus quam pellentesque nec nam.

-

Sed cras ornare arcu dui vivamus arcu: Pellentesque adipiscing commodo elit at. Fusce id velit ut tortor pretium viverra. Nunc vel risus commodo viverra. Dui faucibus in ornare quam viverra orci sagittis eu volutpat. Aliquet nibh praesent tristique magna. Purus sit amet volutpat consequat. Gravida neque convallis a cras. Aenean euismod elementum nisi quis eleifend. At tellus at urna condimentum mattis pellentesque id nibh tortor. Sit amet massa vitae tortor. Volutpat lacus laoreet non curabitur gravida arcu ac. Vulputate dignissim suspendisse in est ante. Tempor commodo ullamcorper a lacus vestibulum. Quis vel eros donec ac odio tempor. Lacus sed turpis tincidunt id aliquet risus feugiat in ante. Metus aliquam eleifend mi in.

' +
+ +
+
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Euismod nisi porta lorem mollis. Congue quisque egestas diam in arcu cursus euismod. Auctor neque vitae tempus quam pellentesque nec nam. Erat imperdiet sed euismod nisi porta lorem. Nunc eget lorem dolor sed viverra ipsum nunc aliquet bibendum. Sed blandit libero volutpat sed cras ornare. Convallis tellus id interdum velit laoreet id. Amet mauris commodo quis imperdiet massa tincidunt. Mi bibendum neque egestas congue quisque egestas diam in. Enim nec dui nunc mattis enim ut tellus elementum sagittis. Cursus mattis molestie a iaculis at erat pellentesque. Ut tellus elementum sagittis vitae et leo.

+
Eu Consequat ac Felis
+

Eu consequat ac felis donec et odio. Eget arcu dictum varius duis at consectetur lorem. Lorem ipsum dolor sit amet consectetur. Turpis egestas integer eget aliquet nibh praesent. Mattis rhoncus urna neque viverra justo nec. Iaculis urna id volutpat lacus laoreet non curabitur gravida arcu. Convallis posuere morbi leo urna molestie at elementum eu. Fermentum et sollicitudin ac orci phasellus egestas tellus. Convallis aenean et tortor at risus. Morbi tristique senectus et netus et malesuada fames ac. Sed vulputate mi sit amet mauris commodo quis. Nisi quis eleifend quam adipiscing vitae proin sagittis nisl. Id venenatis a condimentum vitae sapien pellentesque habitant morbi tristique. Id cursus metus aliquam eleifend mi in nulla. Proin fermentum leo vel orci porta non pulvinar neque laoreet. Lobortis mattis aliquam faucibus purus in massa tempor. Varius vel pharetra vel turpis nunc.

+

Mauris rhoncus aenean vel elit: Blandit aliquam etiam erat velit. Auctor neque vitae tempus quam pellentesque nec nam. Augue mauris augue neque gravida in fermentum et. Tempus urna et pharetra pharetra. Vel turpis nunc eget lorem. Vitae nunc sed velit dignissim. Enim tortor at auctor urna nunc id. Pellentesque habitant morbi tristique senectus et netus et. Tellus integer feugiat scelerisque varius morbi enim nunc faucibus.

+

Blandit turpis cursus in hac habitasse platea: Tellus elementum sagittis vitae et leo duis ut diam quam. Amet nisl purus in mollis nunc sed. Ac feugiat sed lectus vestibulum. Suscipit adipiscing bibendum est ultricies integer quis. Tortor pretium viverra suspendisse potenti nullam ac tortor. Blandit turpis cursus in hac. Id porta nibh venenatis cras sed felis eget velit. Fermentum posuere urna nec tincidunt praesent semper feugiat nibh sed. Pellentesque elit ullamcorper dignissim cras tincidunt lobortis feugiat vivamus at. Sapien et ligula ullamcorper malesuada proin libero nunc consequat. Aliquet enim tortor at auctor urna nunc id. Fringilla ut morbi tincidunt augue interdum velit euismod in. In arcu cursus euismod quis viverra nibh. Vulputate ut pharetra sit amet. Purus in massa tempor nec. Pellentesque massa placerat duis ultricies lacus sed. Integer feugiat scelerisque varius morbi enim. Vitae tempus quam pellentesque nec nam.

+

Sed cras ornare arcu dui vivamus arcu: Pellentesque adipiscing commodo elit at. Fusce id velit ut tortor pretium viverra. Nunc vel risus commodo viverra. Dui faucibus in ornare quam viverra orci sagittis eu volutpat. Aliquet nibh praesent tristique magna. Purus sit amet volutpat consequat. Gravida neque convallis a cras. Aenean euismod elementum nisi quis eleifend. At tellus at urna condimentum mattis pellentesque id nibh tortor. Sit amet massa vitae tortor. Volutpat lacus laoreet non curabitur gravida arcu ac. Vulputate dignissim suspendisse in est ante. Tempor commodo ullamcorper a lacus vestibulum. Quis vel eros donec ac odio tempor. Lacus sed turpis tincidunt id aliquet risus feugiat in ante. Metus aliquam eleifend mi in.

' +
-
- - -
-
- - - + + +
+
+ + + +
+
- -
-
-
-
{% endblock %} + +{% block after_app_content %} + {{ block.super }} + {% include "earth_engine/disclaimer.html" %} +{% endblock %} \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/base.html b/tethysapp/earth_engine/templates/earth_engine/base.html index 3bf2e2b..6ca00b0 100644 --- a/tethysapp/earth_engine/templates/earth_engine/base.html +++ b/tethysapp/earth_engine/templates/earth_engine/base.html @@ -13,29 +13,20 @@ {% block app_title %}{{ tethys_app.name }}{% endblock %} {% block header_buttons %} - {{ block.super }} -
- -
-
- -
-
- -
+ {% include "earth_engine/header_buttons.html" %} {% endblock %} {% block app_navigation_items %} -
  • App Navigation
  • -
  • Home
  • -
  • Jobs
  • -
  • Results
  • -
  • Steps
  • -
  • 1. The First Step
  • -
  • 2. The Second Step
  • -
  • 3. The Third Step
  • -
  • -
  • Get Started
  • + + + + + + + + + + {% endblock %} {% block app_content %} @@ -44,51 +35,17 @@ {% block app_actions_override %} {% endblock %} +{% block after_app_content %} + {{ block.super }} + {% include "earth_engine/disclaimer.html" %} +{% endblock %} + {% block content_dependent_styles %} {{ block.super }} - {% endblock %} {% block scripts %} {{ block.super }} -{% endblock %} - -{# Use the after_app_content block for modals #} -{% block after_app_content %} - {{ block.super }} - - - -{% endblock %} +{% endblock %} \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/disclaimer.html b/tethysapp/earth_engine/templates/earth_engine/disclaimer.html new file mode 100644 index 0000000..78b9e17 --- /dev/null +++ b/tethysapp/earth_engine/templates/earth_engine/disclaimer.html @@ -0,0 +1,58 @@ + + + \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/header_buttons.html b/tethysapp/earth_engine/templates/earth_engine/header_buttons.html new file mode 100644 index 0000000..8f6661d --- /dev/null +++ b/tethysapp/earth_engine/templates/earth_engine/header_buttons.html @@ -0,0 +1,9 @@ +
    + +
    +
    + +
    +
    + +
    \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/home.html b/tethysapp/earth_engine/templates/earth_engine/home.html index a091c89..eca72b3 100644 --- a/tethysapp/earth_engine/templates/earth_engine/home.html +++ b/tethysapp/earth_engine/templates/earth_engine/home.html @@ -1,21 +1,21 @@ -{% extends "earth_engine/base.html" %} +{% extends "tethys_apps/app_header_content.html" %} {% load tethys_gizmos static %} {% block styles %} {{ block.super }} - {% endblock %} -{% block app_navigation_override %} +{% block header_buttons %} + {% include "earth_engine/header_buttons.html" %} {% endblock %} {% block app_content %}
    -
    -

    About

    +
    +

    About

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Eget est lorem ipsum dolor sit amet. Morbi tincidunt augue interdum velit euismod in pellentesque.

    Ac felis donec et odio pellentesque. Quis ipsum suspendisse ultrices gravida dictum fusce ut. Curabitur gravida arcu ac tortor dignissim convallis aenean et tortor. Sed euismod nisi porta lorem mollis. Nisi scelerisque eu ultrices vitae. Sit amet consectetur adipiscing elit duis. At in tellus integer feugiat scelerisque varius morbi enim.

    @@ -26,35 +26,37 @@

    About

    Resources

    -
    -
    +
    + -
    +

    Lorem Ipsum Dolor

    Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
    -
    -
    + +
    + -
    +

    Ut Enim Ad Minim

    Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
    -
    -
    + +
    + -
    +

    Duis Aute Irure

    Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
    @@ -64,17 +66,19 @@

    Duis Aute Irure

    -
    +

    Get Started

    Press the button below to launch the viewer

    Launch Viewer
    -
    +
    {% endblock %} - - +{% block after_app_content %} + {{ block.super }} + {% include "earth_engine/disclaimer.html" %} +{% endblock %} \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/plot.html b/tethysapp/earth_engine/templates/earth_engine/plot.html index 0c84710..383985d 100644 --- a/tethysapp/earth_engine/templates/earth_engine/plot.html +++ b/tethysapp/earth_engine/templates/earth_engine/plot.html @@ -8,4 +8,4 @@ -{% endif %} +{% endif %} \ No newline at end of file diff --git a/tethysapp/earth_engine/templates/earth_engine/viewer.html b/tethysapp/earth_engine/templates/earth_engine/viewer.html index 426d9ad..2052f23 100644 --- a/tethysapp/earth_engine/templates/earth_engine/viewer.html +++ b/tethysapp/earth_engine/templates/earth_engine/viewer.html @@ -1,13 +1,6 @@ {% extends "earth_engine/base.html" %} {% load tethys_gizmos static %} -{% block content_dependent_styles %} - {{ block.super }} - - - -{% endblock %} - {% block import_gizmos %} {% import_gizmo_dependency plotly_view %} {% endblock %} @@ -23,9 +16,9 @@

    Change variables to select a data product, then press "Load" to add that product to the map.

    {% gizmo load_button %} {% gizmo clear_button %} -

    Draw an area of interest or drop a point, the press "Plot AOI" to view a plot of the data.

    +

    Draw an area of interest or drop a point, the press "Plot AOI" to view a plot of the data.

    {% gizmo plot_button %} -

    Upload a shapefile of a boundary to use to clip datasets and set the default extent.

    +

    Upload a shapefile of a boundary to use to clip datasets and set the default extent.

    {% gizmo set_boundary_button %} {% endblock %} @@ -41,8 +34,8 @@