Skip to content

Commit

Permalink
convert times to seconds since epoch within js
Browse files Browse the repository at this point in the history
add attributions within imageservicelayer notebook

use widget controls for raster functions

use widget control to show different years
  • Loading branch information
tsutterley committed Apr 19, 2022
1 parent cb9fb67 commit ae41b97
Show file tree
Hide file tree
Showing 6 changed files with 184 additions and 23 deletions.
33 changes: 33 additions & 0 deletions docs/source/layers/image_service_layer.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
Image Service Layer
===================

Example
-------

.. jupyter-execute::

from ipyleaflet import Map, ImageServiceLayer, basemaps

im = ImageServiceLayer(
url='https://landsat.arcgis.com/arcgis/rest/services/Landsat/PS/ImageServer',
rendering_rule={rasterFunction:"Pansharpened Enhanced with DRA"},
format='jpgpng',
attribution='United States Geological Survey (USGS), National Aeronautics and Space Administration (NASA)'
)

m = Map(basemap=basemaps.Esri.WorldTopoMap, center=(47.655548, -122.303200), zoom=12)

m.add_layer(im)

m

Usage
-----

By default, options like ``format``, ``band_ids``, ``time``, ``rendering_rule`` are appended to the request URL when making the image service layer request.

Attributes
----------

.. autoclass:: ipyleaflet.leaflet.ImageServiceLayer
:members:
1 change: 1 addition & 0 deletions docs/source/layers/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ Layers
popup
wms_layer
image_video_overlay
image_service_layer
antpath
polyline
polygon
Expand Down
128 changes: 117 additions & 11 deletions examples/ImageServiceLayer.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"source": [
"## Image Service Layer\n",
"\n",
"This notebook shows how you can overlay an images from an ESRI Image Server on a Leaflet map"
"This notebook shows how you can overlay images from an ESRI Image Server on a Leaflet map"
]
},
{
Expand All @@ -15,21 +15,64 @@
"metadata": {},
"outputs": [],
"source": [
"import time\n",
"from ipywidgets import Dropdown\n",
"from ipyleaflet import (\n",
" Map,\n",
" basemaps,\n",
" ImageServiceLayer,\n",
" projections,\n",
" WidgetControl\n",
")\n",
"\n",
"# Columbia Glacier with Landsat\n",
"m1 = Map(\n",
" center=[47.655548, -122.303200],\n",
" zoom=12,\n",
" center=[61.1, -146.9],\n",
" zoom=10,\n",
" basemap=basemaps.Esri.WorldTopoMap,\n",
")\n",
"# add image service layer with Landsat pan-sharpened imagery\n",
"im1 = ImageServiceLayer(url='https://landsat.arcgis.com/arcgis/rest/services/Landsat/PS/ImageServer')\n",
"\n",
"# create a widget control for years of observations\n",
"years = [\n",
" ['1980-01-01','1989-12-31'],\n",
" ['2000-01-01','2009-12-31'],\n",
" ['2010-01-01','2019-12-31']\n",
"]\n",
"def range_formatter(d):\n",
" st = time.strptime(d[0],'%Y-%m-%d')\n",
" et = time.strptime(d[1],'%Y-%m-%d')\n",
" return u'{0:d}\\u2013{1:d}'.format(st.tm_year,et.tm_year)\n",
"\n",
"range_dropdown1 = Dropdown(\n",
" value=u'{0:d}\\u2013{1:d}'.format(1980,1989),\n",
" options=[range_formatter(d) for d in years],\n",
" description=\"Years:\",\n",
")\n",
"\n",
"# add image service layer with Landsat multi-spectral imagery\n",
"# create a false color image to highlight ice and snow\n",
"url = 'https://landsat.arcgis.com/arcgis/rest/services/LandsatGLS/MS/ImageServer'\n",
"attribution = \"\"\"United States Geological Survey (USGS),\n",
" National Aeronautics and Space Administration (NASA)\"\"\"\n",
"im1 = ImageServiceLayer(url=url,\n",
" band_ids=['5','4','2'],\n",
" time=years[range_dropdown1.index],\n",
" attribution=attribution)\n",
"m1.add_layer(im1)\n",
"\n",
"# add control for year range\n",
"widget_control1 = WidgetControl(widget=range_dropdown1, position=\"topright\")\n",
"m1.add_control(widget_control1)\n",
"\n",
"# set the year range\n",
"def set_year_range(sender):\n",
" im1.time = years[range_dropdown1.index]\n",
" # force redrawing of map by removing and adding layer\n",
" m1.remove_layer(im1)\n",
" m1.add_layer(im1)\n",
"\n",
"# watch year range function widget for changes\n",
"range_dropdown1.observe(set_year_range)\n",
"m1"
]
},
Expand All @@ -39,6 +82,7 @@
"metadata": {},
"outputs": [],
"source": [
"# ArcticDEM\n",
"# note that we need to use the same projection for the our image service layer and the map.\n",
"m2 = Map(\n",
" center=(90, 0),\n",
Expand All @@ -47,13 +91,44 @@
" crs=projections.EPSG5936.Basemap,\n",
")\n",
"m2.add_layer(basemaps.Esri.ArcticOceanReference)\n",
"\n",
"# create a widget control for the raster function\n",
"raster_functions = [\n",
" \"Aspect Map\",\n",
" \"Contour 25\",\n",
" \"Hillshade Elevation Tinted\",\n",
" \"Hillshade Gray\",\n",
" \"Height Ellipsoidal\",\n",
" \"Height Orthometric\",\n",
" \"Slope Map\"]\n",
"raster_dropdown2 = Dropdown(\n",
" value=\"Hillshade Gray\",\n",
" options=raster_functions,\n",
" description=\"Raster:\",\n",
")\n",
"\n",
"# add image service layer with ArcticDEM\n",
"url = 'https://elevation2.arcgis.com/arcgis/rest/services/Polar/ArcticDEM/ImageServer'\n",
"rendering_rule = {\"rasterFunction\": \"Height Orthometric\"}\n",
"rendering_rule = {\"rasterFunction\": raster_dropdown2.value}\n",
"im2 = ImageServiceLayer(url=url,\n",
" format='png', rendering_rule=rendering_rule,\n",
" crs=projections.EPSG3031.Basemap)\n",
"m2.add_layer(im2)\n",
" format='jpgpng', rendering_rule=rendering_rule,\n",
" attribution='PGC, UMN, Esri',\n",
" crs=projections.EPSG5936.Basemap)\n",
"m2.add_layer(im2) \n",
"\n",
"# add control for raster function\n",
"widget_control2 = WidgetControl(widget=raster_dropdown2, position=\"topright\")\n",
"m2.add_control(widget_control2)\n",
"\n",
"# set the rendering rule\n",
"def set_raster_function2(sender):\n",
" im2.rendering_rule = {\"rasterFunction\": raster_dropdown2.value}\n",
" # force redrawing of map by removing and adding layer\n",
" m2.remove_layer(im2)\n",
" m2.add_layer(im2)\n",
"\n",
"# watch raster function widget for changes\n",
"raster_dropdown2.observe(set_raster_function2)\n",
"m2"
]
},
Expand All @@ -63,20 +138,51 @@
"metadata": {},
"outputs": [],
"source": [
"# Reference Elevation Model of Antarctica (REMA)\n",
"# note that we need to use the same projection for the our image service layer and the map.\n",
"m3 = Map(\n",
" center=(-90, 0),\n",
" zoom=3,\n",
" basemap=basemaps.Esri.AntarcticBasemap,\n",
" crs=projections.EPSG3031.Basemap,\n",
")\n",
"\n",
"# create a widget control for the raster function\n",
"raster_functions = [\n",
" \"Aspect Map\",\n",
" \"Contour 25\",\n",
" \"Hillshade Elevation Tinted\",\n",
" \"Hillshade Gray\",\n",
" \"Height Orthometric\",\n",
" \"Slope Degrees Map\"]\n",
"raster_dropdown3 = Dropdown(\n",
" value=\"Hillshade Gray\",\n",
" options=raster_functions,\n",
" description=\"Raster:\",\n",
")\n",
"\n",
"# add image service layer with REMA imagery\n",
"url = 'https://elevation2.arcgis.com/arcgis/rest/services/Polar/AntarcticDEM/ImageServer'\n",
"rendering_rule = {\"rasterFunction\": \"Hillshade Gray\"}\n",
"rendering_rule = {\"rasterFunction\": raster_dropdown3.value}\n",
"im3 = ImageServiceLayer(url=url,\n",
" format='png', rendering_rule=rendering_rule,\n",
" format='jpgpng', rendering_rule=rendering_rule,\n",
" attribution='PGC, UMN, Esri',\n",
" crs=projections.EPSG3031.Basemap)\n",
"m3.add_layer(im3)\n",
"\n",
"# add control for raster function\n",
"widget_control3 = WidgetControl(widget=raster_dropdown3, position=\"topright\")\n",
"m3.add_control(widget_control3)\n",
"\n",
"# set the rendering rule\n",
"def set_raster_function3(sender):\n",
" im3.rendering_rule = {\"rasterFunction\": raster_dropdown3.value}\n",
" # force redrawing of map by removing and adding layer\n",
" m3.remove_layer(im3)\n",
" m3.add_layer(im3)\n",
"\n",
"# watch raster function widget for changes\n",
"raster_dropdown3.observe(set_raster_function3)\n",
"m3"
]
}
Expand Down
17 changes: 13 additions & 4 deletions ipyleaflet/leaflet.py
Original file line number Diff line number Diff line change
Expand Up @@ -770,7 +770,7 @@ class VideoOverlay(RasterLayer):
attribution = Unicode().tag(sync=True, o=True)


class ImageServiceLayer(RasterLayer):
class ImageServiceLayer(Layer):
"""ImageServiceLayer class
Image Service layer for raster data served through a web service
Expand All @@ -781,9 +781,9 @@ class ImageServiceLayer(RasterLayer):
Url to the image service
f: string, default "image"
response format (use `'image'` to stream as bytes)
format: string, default "png"
format of exported image (use `'png'` for layers with transparency)
pixel_type: string, default "F32"
format: string, default "jpgpng"
format of exported image
pixel_type: string, default "UNKNOWN"
data type of the raster image
no_data: list, default []
pixel value or comma-delimited list of pixel values representing no data
Expand All @@ -803,8 +803,14 @@ class ImageServiceLayer(RasterLayer):
rules for mosaicking
transparent: boolean, default False
If true, the image service will return images with transparency
attribution: string, default ""
Image service attribution.
crs: dict, default ipyleaflet.projections.EPSG3857
Projection used for this image service.
interactive: bool, default False
Emit when clicked or hovered
update_interval: int, default 200
Update interval for panning
"""

_view_name = Unicode('LeafletImageServiceLayerView').tag(sync=True)
Expand Down Expand Up @@ -835,7 +841,10 @@ class ImageServiceLayer(RasterLayer):
rendering_rule = Dict({}).tag(sync=True, o=True)
mosaic_rule = Dict({}).tag(sync=True, o=True)
transparent = Bool(False).tag(sync=True, o=True)
attribution = Unicode('').tag(sync=True, o=True)
crs = Dict(default_value=projections.EPSG3857).tag(sync=True)
interactive = Bool(False).tag(sync=True, o=True)
update_interval = Int(200).tag(sync=True, o=True)


class Heatmap(RasterLayer):
Expand Down
8 changes: 5 additions & 3 deletions js/src/layers/ImageServiceLayer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,10 @@
// Distributed under the terms of the Modified BSD License.

const L = require('../leaflet.js');
const rasterlayer = require('./RasterLayer.js');
const layer = require('./Layer.js');
const proj = require('../projections.js');

export class LeafletImageServiceLayerModel extends rasterlayer.LeafletRasterLayerModel {
export class LeafletImageServiceLayerModel extends layer.LeafletLayerModel {
defaults() {
return {
...super.defaults(),
Expand Down Expand Up @@ -37,6 +37,8 @@ export class LeafletImageServiceLayerModel extends rasterlayer.LeafletRasterLaye
mosaicRule: {},
// image transparency
transparent: false,
// image service attribution
attribution: '',
// coordinate reference system
crs: null,
// emit when clicked or hovered
Expand All @@ -47,7 +49,7 @@ export class LeafletImageServiceLayerModel extends rasterlayer.LeafletRasterLaye
}
}

export class LeafletImageServiceLayerView extends rasterlayer.LeafletRasterLayerView {
export class LeafletImageServiceLayerView extends layer.LeafletLayerView {
create_obj() {
this.obj = L.imageServiceLayer({
url: this.model.get('url'),
Expand Down
20 changes: 15 additions & 5 deletions js/src/leaflet-imageservice.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ L.ImageServiceLayer = L.Layer.extend({
mosaicRule: {},
// image transparency
transparent: false,
// image service attribution
attribution: '',
// coordinate reference system
crs: null,
// emit when clicked or hovered
Expand All @@ -53,7 +55,7 @@ L.ImageServiceLayer = L.Layer.extend({
this._map = map;
this.updateUrl();
if (!this._image) {
this._initImage();
this._initImage();
}
this._map.on('moveend', () => {
L.Util.throttle(this._update(),this.options.updateInterval,this);
Expand Down Expand Up @@ -125,7 +127,8 @@ L.ImageServiceLayer = L.Layer.extend({
},

getCenter: function () {
return this._bounds.getCenter();
// get map center
return this._bounds.getCenter();
},

_getBBox: function() {
Expand Down Expand Up @@ -160,11 +163,18 @@ L.ImageServiceLayer = L.Layer.extend({
return spatial_reference;
},

_getTime: function() {
// get start and end times and convert to seconds since epoch
var st = new Date(this.options.time[0]).getTime().valueOf();
var et = new Date(this.options.time[1]).getTime().valueOf();
return [st, et];
},

_buildParams: function() {
// parameters for image server query
var params = {
bbox: this._getBBox(),
size: this._getSize(),
bbox: this._getBBox().join(','),
size: this._getSize().join(','),
bboxSR: this._getEPSG(),
imageSR: this._getEPSG(),
f: this.options.f,
Expand Down Expand Up @@ -196,7 +206,7 @@ L.ImageServiceLayer = L.Layer.extend({
params['bandIds'] = this.options.bandIds.join(',');
}
if (this.options.time.length) {
params['time'] = this.options.time.join(',');
params['time'] = this._getTime().join(',');
}
// convert dictionary parameters to JSON
if (Object.keys(this.options.renderingRule).length) {
Expand Down

0 comments on commit ae41b97

Please sign in to comment.