UNPKG

@dhis2/gis-api

Version:

Maps API for DHIS2 based on Leaflet

298 lines (247 loc) 8.53 kB
import L from 'leaflet' import layerMixin from './layerMixin' // Leaflet plugin to add map layers from Google Earth Engine // LayerGroup is used as a Google Earth Engine visualization can consists of more than one tilelayer export const EarthEngine = L.LayerGroup.extend({ ...layerMixin, options: { url: 'https://earthengine.googleapis.com/map/{mapid}/{z}/{x}/{y}?token={token}', tokenType: 'Bearer', aggregation: 'none', popup: '{name}: {value} {unit}', }, initialize(options) { L.setOptions(this, { ...options, pane: options.id }) this._layers = {} this._legend = options.legend || this.createLegend() }, onAdd() { this.setAuthToken() }, onRemove(map) { if (this._popup) { map.closePopup(this._popup) } L.GeoJSON.prototype.onRemove.call(this, map) }, // Configures client-side authentication of EE API calls by providing a OAuth2 token to use. async setAuthToken() { const { accessToken, tokenType } = this.options if (accessToken) { const token = await accessToken if (token) { const { access_token, client_id, expires_in } = token ee.data.setAuthToken( client_id, tokenType, access_token, expires_in ) ee.data.setAuthTokenRefresher( this.refreshAccessToken.bind(this) ) ee.initialize(null, null, this.createImage.bind(this)) } } }, // Refresh OAuth2 token when expired refreshAccessToken(authArgs, callback) { const { tokenType } = this.options this.getAuthToken(token => { callback({ token_type: tokenType, access_token: token.access_token, state: authArgs.scope, expires_in: token.expires_in, }) }) }, // Create EE tile layer from params (override for each layer type) createImage() { // eslint-disable-line const options = this.options let eeCollection let eeImage if (options.filter) { // Image collection eeCollection = ee.ImageCollection(options.datasetId) // eslint-disable-line eeCollection = this.applyFilter(eeCollection) if (options.aggregation === 'mosaic') { this.eeCollection = eeCollection eeImage = eeCollection.mosaic() } else { eeImage = ee.Image(eeCollection.first()) // eslint-disable-line } } else { // Single image eeImage = ee.Image(options.datasetId) // eslint-disable-line } if (options.band) { eeImage = eeImage.select(options.band) } if (options.mask) { // Mask out 0-values eeImage = eeImage.updateMask(eeImage.gt(0)) } // Run methods on image eeImage = this.runMethods(eeImage) this.eeImage = eeImage // Classify image if (!options.legend) { // Don't classify if legend is provided eeImage = this.classifyImage(eeImage) } this.addLayer(this.visualize(eeImage)) }, // Add EE image to map as TileLayer addLayer(eeImage) { eeImage.getMap(null, eeMap => { const layer = L.tileLayer( this.options.url, L.extend( { token: eeMap.token, mapid: eeMap.mapid, }, this.options ) ) L.LayerGroup.prototype.addLayer.call(this, layer) this.setIndex(this.options.index) }) }, applyFilter(collection, filterOpt) { const filter = filterOpt || this.options.filter if (filter) { filter.forEach(item => { collection = collection.filter( ee.Filter[item.type].apply(this, item.arguments) ) // eslint-disable-line }) } return collection }, // Run methods on image // https://code.earthengine.google.com/a19f5cec73720aba049b457d55672cee // https://code.earthengine.google.com/37e4e9cc4436a22e5c3e0f63acb4c0bc runMethods(image) { const methods = this.options.methods let eeImage = image if (methods) { Object.keys(methods).forEach(method => { if (eeImage[method]) { // Make sure method exist eeImage = eeImage[method].apply(eeImage, methods[method]) // eslint-disable-line } }) } return eeImage }, // Classify image according to legend classifyImage(eeImage) { const legend = this._legend let zones for (let i = 0, item; i < legend.length - 1; i++) { item = legend[i] if (!zones) { zones = eeImage.gt(item.to) } else { zones = zones.add(eeImage.gt(item.to)) } } return zones }, // Visualize image (turn into RGB) visualize(eeImage) { const options = this.options return eeImage.visualize( options.legend ? options.params : { min: 0, max: this._legend.length - 1, palette: options.params.palette, } ) }, createLegend() { const params = this.options.params const min = params.min const max = params.max const palette = params.palette.split(',') const step = (params.max - min) / (palette.length - (min > 0 ? 2 : 1)) let from = min let to = Math.round(min + step) return palette.map((color, index) => { const item = { color } if (index === 0 && min > 0) { // Less than min item.from = 0 item.to = min item.name = '< ' + item.to to = min } else if (from < max) { item.from = from item.to = to item.name = item.from + ' - ' + item.to } else { // Higher than max item.from = from item.name = '> ' + item.from } from = to to = Math.round(min + step * (index + (min > 0 ? 1 : 2))) return item }) }, setOpacity(opacity) { this.options.opacity = opacity this.invoke('setOpacity', opacity) }, // Returns value at location in a callback getValue(latlng, callback) { const point = ee.Geometry.Point(latlng.lng, latlng.lat) // eslint-disable-line const options = this.options let dictionary if (options.aggregation === 'mosaic') { dictionary = this.eeImage.reduceRegion( ee.Reducer.mean(), point, options.resolution, options.projection ) // eslint-disable-line } else { dictionary = this.eeImage.reduceRegion(ee.Reducer.mean(), point) // eslint-disable-line } dictionary.getInfo(valueObj => { const band = options.band || Object.keys(valueObj)[0] let value = valueObj[band] if (options.legend && options.legend[value]) { value = options.legend[value].name } else if (options.value) { // Needs calculation value = options.value(value) } callback(value) }) }, // Shows the value at location (popup) showValue(latlng) { const options = this.options this.getValue(latlng, value => { this._popup = L.popup() .setLatLng(latlng) .setContent( L.Util.template( options.popup, L.extend({}, options, { value }) ) ) .openOn(this._map) }) }, }) export default function earthEngine(options) { return new EarthEngine(options) }