GEOG 6161 Capstone

JavaScript Code for Google Earth Engine Application

 

//Repository script to hold components of Pelican Tracking app
//App is still in development
var table = ee.FeatureCollection("users/1halliejohnson/cleandata_042120")
//This function filters out cloud and cloud shadow pixels
function maskL8sr(image) {
  // Bits 3 and 5 are cloud shadow and cloud, respectively.
  var cloudShadowBitMask = (1 << 3);
  var cloudsBitMask = (1 << 5);
  // Get the pixel QA band.
  var qa = image.select('pixel_qa');
  // Both flags should be set to zero, indicating clear conditions.
  var mask = qa.bitwiseAnd(cloudShadowBitMask).eq(0)
                 .and(qa.bitwiseAnd(cloudsBitMask).eq(0));
  return image.updateMask(mask);
}
//assign a now variable to allow the range for the landsat image to continue
var now = Date.now();
//Landsat 8 Image collection with the cloud mask applied and filter dates
var collection = ee.ImageCollection('LANDSAT/LC08/C01/T1_SR')
                  .filterDate('2014-01-01', now)
                  .map(maskL8sr);
                  
                  
Map.setCenter(-112.523386, 41.429234, 6);

//Below are three dictionaries, currently the month color dictionary
//is being used, but there is framework to redeploy with the other styling 
//options
var sexcolor = ee.Dictionary({
  "M":'#d73027',
  'F': '#313695',
});
//currently in use in app
var monthcolor = ee.Dictionary({
  '1': '#313695',
  '2': '#313695',
  '3': '#abd9e9',
  '4': '#abd9e9',
  '5': '#abd9e9',
  '6': '#d73027',
  '7': '#d73027',
  '8': '#d73027',
  '9': '#fdae61',
  '10':'#fdae61',
  '11':'#fdae61',
  '12':'#313695',
});

var pelicolor = ee.Dictionary({
  'Annabelle':'#1F78B4',
  'Barnabus':'#B2DF8A', 
  'Chester':'#E31A1C', 
  'Daphne':'#FDBF6F', 
  'Eleanor':'#CAB2D6',
  'Finnigan':'#FB9A99', 
  'George':'#565A99', 
  'Abigail':'#A6CEE3', 
  'Cecilia':'#FF5699', 
  'Bartholomew':'#33A02C', 
  'Derek':'#FF7F00',
  'Gerald':'#CC5019', 
  'Hector':'#75FF00', 
  'Indie':'#ED8EFF', 
  'Jerome':'#7BB297',
  'Everett':'#383DD3', 
  'Flagg':'#B15928',
  'Loretta': '#BB41C9', 
  'Manuel':'#CB367E', 
  'Kirk':'#FFECBF', 
  'Oscar':'#E67878', 
  'Nelson':'#66CAEA', 
  'Pedro':'#63B8E3', 
  'Quincy':'#5F5FD7',
  'Rosalie':'#FA418C', 
  'Sylvester':'#FAA569', 
  'Theodore':'#AFCD78', 
  'Waylon':'#4350FF', 
  'Vinnie':'#32CC78', 
  'Uma':'#009A2D',
  'Xavier':'#21F6A1', 
  'Yukiko':'#ffff00', 
  'Albus':'#b71b40', 
  'Bridgette':'#fc0927', 
  'Fiona':'#bbf02c', 
  'Eloise':'#abe1d4',
  'Deidra':'#e7d60d', 
  'Cici':'#f7d6a2', 
  'Hook':'#FF1493', 
  'Genevieve':'#f56e3f', 
  'Kevin':'#c57779', 
  'Lupita':'#418af1', 
  'Jonah':'#8A2BE2',
  'Iris':'#217906', 
  'Miguel':'#a6cee3', 
  'Nathan':'#b2df8a', 
  'Penelope':'#40E0D0', 
  'Quentin':'#1f78b4', 
  'Olivia':'#6a3d9a',
  'Winona':'#ffff99', 
  'Xena':'#fdbf6f', 
  'Tabitha':'#cab2d6', 
  'Valerie':'#e31a1c', 
  'Stephan':'#fb9a99', 
  'Rachelle':'#ff7f00',
  'Ursula':'#c6499b', 
  'Yogi':'#a24a51',  
  'Alexander':'#2bbfa8', 
  'Zeus':'#a32bd4', 
  'Bianca':'#d6e368', 
  'Cassandra':'#94d576',
  'Darla':'#ad0c13', 
  'Gregory':'#9fedd9', 
  'Elmer':'#db5c2a', 
  'Fudd':'#00ea8d', 
  'Aurora':'#00ea8d',///this isn't the right color, i think i shifted them down one
});

var pname = ['Annabelle', 'Barnabus', 'Chester', 'Daphne', 'Eleanor',
       'Finnigan', 'George', 'Abigail', 'Cecilia', 'Bartholomew', 'Derek',
       'Gerald', 'Hector', 'Indie', 'Jerome', 'Everett', 'Flagg',
       'Loretta', 'Manuel', 'Kirk', 'Oscar', 'Nelson', 'Pedro', 'Quincy',
       'Rosalie', 'Sylvester', 'Theodore', 'Waylon', 'Vinnie', 'Uma',
       'Xavier', 'Yukiko', 'Albus', 'Bridgette', 'Fiona', 'Eloise',
       'Deidra', 'Cici', 'Hook', 'Genevieve', 'Kevin', 'Lupita', 'Jonah',
       'Iris', 'Miguel', 'Nathan', 'Penelope', 'Quentin', 'Olivia',
       'Winona', 'Xena', 'Tabitha', 'Valerie', 'Stephan', 'Rachelle',
       'Ursula', 'Yogi', 'Alexander', 'Zeus', 'Bianca', 'Cassandra',
       'Darla', 'Gregory', 'Elmer', 'Fudd', 'Aurora'];

var months = ['1', '2', '3', '4', '5', '6', '7', '8', '9', '10', '11', '12'];
var sexes = ['M','F' ];
//legend properties
var layerProperties = {
 'Season': {
    name: 'table',
    legend: [
      {'Dec - Feb': '#313695'}, {'Mar - May': '#abd9e9'}, {'Jun - Aug': '#d73027'},
      {'Sep - Nov': '#fdae61'},
    
    ],
    defaultVisibility: false
  }
}

//highlight color for identify selection - refernce call function
var HIGHLIGHT_STYLE = {color: '#65FF33', pointSize: 5, fillColor: '#65FF33'};

//Applies the styles from the three dictionaries to the layer - remove 
//comments from other layers to apply, update the return for each variable
function addStyle(pt) {
  
  var pcolor = pelicolor.get(pt.get('name'));
  //var scolor = sexcolor.get(pt.get('sex'));
  var mcolor = monthcolor.get(pt.get('MM'));
  return pt.set('styleProperty', ee.Dictionary({'color': mcolor}));
}
//creates variable of the data with the style applied      
var peli = ee.FeatureCollection(table).map(addStyle);//.filter(ee.Filter.eq('BoolLoc', '1'));
var pelicans = ee.FeatureCollection(table);

//this is called in the app
function addpeliLayer(name){
  print(name);
  Map.addLayer(peli.filter(ee.Filter.eq('name', name)).style({styleProperty: 'styleProperty', neighborhood: 10}), {}, name, false, 0.85);
}

function addsexLayer(sex){
  print(sex);
  Map.addLayer(peli.filter(ee.Filter.eq('sex', sex)).style({styleProperty:'styleProperty', neighborhood: 50}), {}, sex, false, 1.0);
}

function addtimeLayer(MM){
  print(MM);
  Map.addLayer(peli.filter(ee.Filter.eq('MM', MM)).style({styleProperty:'styleProperty', neighborhood: 10}), {}, MM, true, 0.65);
}
//Add pelican data to map
pelicolor.keys().getInfo().map(addpeliLayer);
//sexcolor.keys().getInfo().map(addsexLayer);
//monthcolor.keys().getInfo().map(addtimeLayer);

// Create a map panel.
var mapPanel = ui.Map();
Map.style().set({cursor: 'crosshair'});
// Add a title and some explanatory text to a side panel.
var header = ui.Label('Pelican Seasonal Movement', {fontSize: '36px', color: 'black'});

//Add a lager catch all panel
var toolPanel = ui.Panel([header], 'flow', {width: '200px'});
ui.root.widgets().add(toolPanel);

// Create a layer selector pulldown. There is an option here to add additional layers
// however the visualization is defined by a function and not the layer properties
// The elements of the pulldown are the keys of the layerProperties dictionary.
var selectItems = Object.keys(layerProperties);
// Define the pulldown menu.  Changing the pulldown menu changes the map layer
// and legend.
var layerSelect = ui.Select({
  items: selectItems,
  value: selectItems[0],
  onChange: function(selected) {
    // Loop through the map layers and compare the selected element to the name
    // of the layer. If they're the same, show the layer and set the
    // corresponding legend.  Hide the others.
    mapPanel.layers().forEach(function(element, index) {
      element.setShown(selected == element.getName());
    });
    setLegend(layerProperties[selected].legend);
  }
});
// Add the select to the toolPanel with some explanatory text.
toolPanel.add(ui.Label('Select pelicans from Layers to view in map', {'font-size': '18px'}));
toolPanel.add(layerSelect);

// Create the legend.
// Define a panel for the legend and give it a tile.
var legendPanel = ui.Panel({
  style:
      {fontWeight: 'bold', fontSize: '10px', margin: '0 0 0 8px', padding: '0'}
});
toolPanel.add(legendPanel);

var legendTitle = ui.Label(
    'Legend',
    {fontWeight: 'bold', fontSize: '10px', margin: '0 0 4px 0', padding: '0'});
legendPanel.add(legendTitle);

// Define an area for the legend key itself.
// This area will be replaced every time the layer pulldown is changed.
var keyPanel = ui.Panel();
legendPanel.add(keyPanel);

function setLegend(legend) {
  // Loop through all the items in a layer's key property,
  // creates the item, and adds it to the key panel.
  keyPanel.clear();
  for (var i = 0; i < legend.length; i++) {
    var item = legend[i];
    var name = Object.keys(item)[0];
    var color = item[name];
    var colorBox = ui.Label('', {
      backgroundColor: color,
      // Use padding to give the box height and width.
      padding: '8px',
      margin: '0'
    });
    // Create the label with the description text.
    var description = ui.Label(name, {margin: '0 0 4px 6px'});
    keyPanel.add(
        ui.Panel([colorBox, description], ui.Panel.Layout.Flow('horizontal')));
  }
}

// Set the initial legend.
setLegend(layerProperties[layerSelect.getValue()].legend);

//create a title for the date slider
var dateSliderTitle = ui.Label(
    'Date Slider',
    {fontWeight: 'bold', fontSize: '14px', margin: '4px 4px 4px 4px', padding: '1'});
toolPanel.add(dateSliderTitle);
//add a little explanatory text
toolPanel.add(ui.Label('Choose a date to change the Landsat Imagery', {'font-size': '12px'}));
//creates a dictionary of the image start dates and the first associated 
//image
//also activates the date slider
ee.Dictionary({start: collection.first().date(), end: ee.Date(now)})
  .evaluate(renderSlider) 

//function to format date slider.  Images are chosen from 30 day periods
//when a new date is selected, the "renderDateRange() function is called
function renderSlider(dates) {
  var slider = ui.DateSlider({
    start: dates.start.value, 
    end: dates.end.value, 
    period: 30, // Every 30 days change range for weekly or bi weekly ranges
    onChange: renderDateRange
  })
  toolPanel.add(slider)
}
//filters the imagery based on the input from the slider
//applies some vis parameters to make an RGB image
//updates layer
//something with the Map.layers().reset([layer]) updates all the 
//layers with the imagery, so I add back the pelicans in this 
//function.  
function renderDateRange(dateRange) {
  var image = collection
    .filterDate(dateRange.start(), dateRange.end())
    .median()

  var visParams = {
  bands: ['B4', 'B3', 'B2'],
  min: 0,
  max: 3000,
  gamma: 1.4,
  }; 
  //Map.layers().set(0, ui.Map.Layer(overlay));
  var layer = ui.Map.Layer(image, visParams, 'Imagery');
  Map.layers().set(0, layer).reset(0);
  //pelicolor.keys().getInfo().map(addpeliLayer)
}

var exportButton = ui.Button("Export all features as CSV", Export.table.toDrive(table, "Pelican_Data") );
toolPanel.add(exportButton);
// A list of points the user has clicked on, as [lon,lat] tuples.
var selectedPoints = [];

// Returns the list of birds the user has selected.  There is a 1000
//meter on the selection point, this can be adjusted if it's determined
//that's too coarse
function getSelectedBirds() {
  return peli.filterBounds(ee.Geometry.MultiPoint(selectedPoints).buffer(1000));
}

// Updates the map overlay using the currently-selected bird 
function updateOverlay() {
  var overlay = getSelectedBirds().style(HIGHLIGHT_STYLE);
  Map.layers().set(0, ui.Map.Layer(overlay));
}

// Define groups in the data by mapping a function to set a new property.

  
// Makes a bar chart of the given FeatureCollection of countries by name. Change to scatter chart
function makeResultsBarChart(peli) {
  var chart = ui.Chart.feature.byFeature(peli, 'Date_time', 'DispAlt');
  chart.setChartType('ScatterChart');
  chart.setOptions({
    title: 'Pelican Altitudes',
    vAxis: {title: 'Meters'},
    hAxis: {title: 'Date', minValue: 2014}
  });
  chart.style().set({stretch: 'both'});
  return chart;
}


// Makes a table of the given FeatureCollection of countries by name.
function makeResultsTable(peli) {
  var table = ui.Chart.feature.byFeature(peli, 'name');
  table.setChartType('Table');
  table.setOptions({allowHtml: true, pageSize: 5});
  table.style().set({stretch: 'both'});
  return table;
}


function updateChart() {
  var chartBuilder = chartTypeToggleButton.value;
  var chart = chartBuilder(getSelectedBirds());
  resultsPanel.clear().add(chart).add(buttonPanel);
}

function clearResults() {
  selectedPoints = [];
  Map.layers().remove(Map.layers().get(1));
  var instructionsLabel = ui.Label('Select birds to compare altitude.');
  resultsPanel.widgets().reset([instructionsLabel]);
}

// Register a click handler for the map that adds the clicked point to the
// list and updates the map overlay and chart accordingly.
function handleMapClick(location) {
  selectedPoints.push([location.lon, location.lat]);
  updateOverlay();
  updateChart();
}
Map.onClick(handleMapClick);

function ToggleButton(states, onClick) {
  var index = 0;
  var button = ui.Button(states[index].label);
  button.value = states[index].value;
  button.onClick(function() {
    index = ++index % states.length;
    button.setLabel(states[index].label);
    button.value = states[index].value;
    onClick();
  });
  return button;
}

// Our chart type toggle button: the button text is the opposite of the
// current state, since you click the button to switch states.
var chartTypeToggleButton = ToggleButton(
    [
      {
        label: 'Display results as table',
        value: makeResultsBarChart,
      },
      {
        label: 'Display results as chart',
        value: makeResultsTable,
      }
    ],
    updateChart);

// A panel containing the two buttons .
var buttonPanel = ui.Panel(
    [ui.Button('Clear results', clearResults), chartTypeToggleButton],
    ui.Panel.Layout.Flow('horizontal'), {margin: '0 0 0 auto', width: '500px'});

var resultsPanel = ui.Panel({style: {position: 'bottom-left'}});
Map.add(resultsPanel);
clearResults();