Draw Circle on Google Earth Pro

In this tutorial, nosotros'll deep dive into some interesting uses of the new cartoon tools API.

  • We'll learn how to dynamically generate a filigree and save it as a client-side geometry.
  • We'll also build a simple classification tool that classifies based on user-fatigued points.

Note: if you are copy and pasting the code snippets below into the Code Editor, they may not work without drawing a geometry first.

Permit'southward get started!

Background

Before the Cartoon Tools API, geometries had to be rendered using Map.addLayer(geometry), which means they had to round trip to the server whenever the user panned or zoomed on the map. With the new drawing tools API, nosotros tin now render those geometries directly on the map equally if we had drawn them ourselves!

Documentation on the drawing tools API is establish here: https://developers.google.com/earth-engine/ui_widgets#ui.map.drawingtools.

The drawing tools API adds 2 new components to the ui.Map API:

  • ui.Map.DrawingTools, and
  • ui.Map.GeometryLayer.

Together, these manage the geometry editing operations as well as any of the geometry layers on the drawing tools. The drawing tools provide a rich gear up of functionality that allow yous to listen for when the user draws, edits, or deletes a geometry, every bit well equally adding, removing, or selecting geometry layers.

To call back the cartoon tools for a map, but telephone call the drawingTools() method on the map:

          var drawingTools = Map.drawingTools();                  

Unlike the default Map, newly created ui.Maps don't become drawing tools by default, but we can add drawing tools to newly created maps past but calling the drawingTools() method to recollect them:

          var map = ui.Map(); // Prints truthful since drawingTools() adds drawing tools to the map. print(map.drawingTools().getShown()); // Replace the default Map with the newly created map. ui.root.widgets().reset([map]);                  

Notation: yous can but have one cartoon tools on a map at a time.

Use the layers() method on the cartoon tools to remember an ActiveList of the geometry layers. A geometry layer contains a list of geometries that you can obtain by calling the geometries() method on the layer. At that place are two ways to add new geometry layers to the drawing tools:

          var drawingTools = Map.drawingTools();  // Use the addLayer method on the cartoon tools directly. var geometries = [ee.Geometry.Point([0, 0]), ee.Geometry.Rectangle([[0, 0], [ane, 1]])]; drawingTools.addLayer(geometries, 'my_geometry1', 'red');  // Create a new GeometryLayer and add it to the layers list. var layer = ui.Map.GeometryLayer(geometries, 'my_geometry2', 'blueish'); drawingTools.layers().add together(layer);  // Print the list of geometries. print(layer.geometries());  // Add together a geometry to the layer. layer.geometries().add(ee.Geometry.Point(i, 1));                  

There are some prissy configuration options for the drawing tools:

  • setShown( boolean ) changes the visibility of the cartoon tools.
  • setDrawModes(['point', 'line', 'polygon', 'rectangle']) changes the bachelor depict modes to the user.
  • setShape('point') sets the electric current depict shape and starts draw mode.
  • setLinked( boolean ) configures whether geometries are linked to the imports.

Let's accept a deeper wait at setLinked, since it's surprisingly useful!

  • When linked mode is turned on, the geometries in the cartoon tools are linked to the imports panel.
  • When linked mode is turned off, the geometries in the imports panel volition be removed from the drawing tools (they won't exist deleted), and newly created geometry layers volition not exist added to the imports.

The default Map has linked mode turned on by default whereas a newly created ui.Map has linked mode turned off by default.

Caution: If you delete a geometry layer while linked manner is on (like calling layers().reset()), they will be deleted from the imports!

Example: Linked Maps

Suppose you wanted to brand a divide map where geometries are reflected on both maps. All you accept to do is turn on linked style for both maps' drawing tools and any geometries that are drawn or edited on ane map volition be reflected on the other!

Here is a demo script:

          var map1 = ui.Map(); map1.drawingTools().setLinked(true);  var map2 = ui.Map(); map2.drawingTools().setLinked(true);  ui.Map.Linker([map1, map2]);  ui.root.widgets().reset([ui.SplitPanel({firstPanel: map1, secondPanel: map2})]);  // Now endeavour drawing a geometry on both maps!                  

Linked maps with geometries

Figure 1. A split map with geometries reflected on both sides.

Neat, right? Permit's do something even more than advanced!

Customer Side Geometries

Next upwardly, we'll learn how to dynamically generate a grid in Globe Engine and add it as a client-side geometry then that it won't be rerendered every fourth dimension the map viewport changes.

The drawing tools let you take a server-side geometry and render it on the client. This requires u.s. to use evaluate on the server-side geometries to fetch them from the server and make them bachelor to be fatigued directly on the map.

Too new, your imported geometries now evidence upward in Apps and then y'all only have to run your expensive geometry rendering lawmaking once.

For example, suppose that you rendered a complex geometry in Earth Engine, you can add it to the map equally a client side geometry (with linked mode on). When you publish the app, the prerendered and imported geometry will already be there; no further coding required! Here's an example of that in activity:

Example: Build a Client-Side Grid

Hither'south a link to the total script. Press run to see the generated grid!

Note: this example doesn't piece of work with all projections and doesn't work well with large geometries, so keep that in listen.

Start, we'll need to write a role that generates a grid based on a geometry. Draw a geometry around a region of involvement and and then run the post-obit script:

          // pixelLonLat returns an prototype with each pixel labeled with longitude and // latitude values. var lonLat = ee.Image.pixelLonLat();  // Select the longitude and latitude bands, multiply by a big number and then // truncate them to integers. var lonGrid = lonLat   .select('longitude')   .multiply(10000000)   .toInt();  var latGrid = lonLat   .select('latitude')   .multiply(10000000)   .toInt();  // To produce the filigree, multiply the latitude and longitude images then apply // reduce to vectors at the 10km resolution to group the grid into vectors. var grid = lonGrid   .multiply(latGrid)   .reduceToVectors({     geometry: geometry, // This is undefined until y'all draw a geometry.     scale: 10000,     geometryType: 'polygon',   });  Map.addLayer(grid);                  

Then we enumerate over the generated Features in the FeatureCollection and build a list of LinearRings that nosotros'll utilise to build our GeometryLayer.

          // Fetch the features in the filigree and produce a list of linear rings. // The grid variable is defined in the step higher up. var geometries = filigree.toList(one thousand).map(function(characteristic) {   var featureGeometry = ee.Feature(feature).geometry();   var coordinates = featureGeometry.coordinates().become(0);   return ee.Geometry.LinearRing(coordinates); });                  

Finally, we add the geometries to the map using the drawing tools. Detect that we utilize evaluate to fetch the geometries customer-side in order to add them to the drawing tools.

          var drawingTools = Map.drawingTools(); // Fetch the geometries and so they can exist added to the drawing tools. geometries.evaluate(office(geometriesList) {   var layer = ui.Map.GeometryLayer({     geometries: geometriesList,     name: 'grid',     color: 'black',     shown: truthful, // Show the layer (already defaults to true).     locked: true, // Lock the layer.   });   drawingTools.layers().fix(one, layer); });                  

There! At present we have a filigree that is rendered customer-side that won't go away when the map viewport changes. Notice that the geometry is uneditable; that'southward because we constructed the layer with the locked property set to true.

A map with a grid

Effigy ii. A map with a grid over the San Francisco Bay Area.

We'll refactor the code to utilise functions to arrive more than reusable:

          function makeGrid(geometry, scale) {   // pixelLonLat returns an image with each pixel labeled with longitude and   // latitude values.   var lonLat = ee.Image.pixelLonLat();    // Select the longitude and latitude bands, multiply by a large number then   // truncate them to integers.   var lonGrid = lonLat     .select('longitude')     .multiply(10000000)     .toInt();    var latGrid = lonLat     .select('latitude')     .multiply(10000000)     .toInt();    // To produce the grid, multiply the breadth and longitude images then use   // reduce to vectors at the 10km resolution to grouping the grid into vectors.   return lonGrid     .multiply(latGrid)     .reduceToVectors({       geometry: geometry, // This is undefined until you draw a geometry.       calibration: scale,       geometryType: 'polygon',     }); }  function buildGeometryList(filigree, limit) {   return filigree.toList(limit).map(function(characteristic) {     var featureGeometry = ee.Feature(characteristic).geometry();     var coordinates = featureGeometry.coordinates().get(0);     render ee.Geometry.LinearRing(coordinates);   }); }  var grid = makeGrid(geometry, 10000); // 10 km scale var geometries = buildGeometryList(grid, thou);  var drawingTools = Map.drawingTools(); geometries.evaluate(function(geometriesList) {   var layer = ui.Map.GeometryLayer({     geometries: geometriesList,     proper noun: 'grid',     colour: 'blackness',     shown: truthful, // Show the layer (already defaults to true).     locked: true, // Lock the layer.   });   drawingTools.layers().fix(1, layer); });                  

Since the grid has already been added to the imports, the grid variable is now available for us to use, and so we tin comment out the bit that generated the grid in the get-go place to save united states some computation fourth dimension.

          // var grid = makeGrid(geometry); // var geometries = buildGeometryList(grid);  // var drawingTools = Map.drawingTools(); // geometries.evaluate(role(geometriesList) { //   var layer = ui.Map.GeometryLayer({ //     geometries: geometriesList, //     name: 'grid', //     color: 'black', //     shown: true, // Show the layer (already defaults to truthful). //     locked: truthful, // Lock the layer. //   }); //   drawingTools.layers().set(one, layer); // });                  

If yous publish this as an app, since the imports carry over to Apps, the grid will be there, ready to go!

Event treatment

Finally, the drawing tools API allows the states to heed for when users interact with:

  • individual geometries,
  • layers,
  • or the drawing tools themselves,

and lets us run callback functions when they happen. Here are some useful events you can heed for:

Drawing Tools Events:

  • onShapeChange( callback ) - A draw way is changed, for example point, line, polygon, or rectangle.

Geometry Events (callback called with geometry, layer, and cartoon tools):

  • onDraw( callback ) - a geometry is added to a layer.
  • onEdit( callback ) - a geometry is edited.
  • onErase( callback ) - a geometry is removed.
  • onSelect( callback ) - a geometry is selected.

Layer Events (callback chosen with layer and drawing tools):

  • onLayerAdd( callback ) - a layer is added.
  • onLayerRemove( callback ) - a layer is removed.
  • onLayerSelect( callback ) - a layer is selected.
  • onLayerConfig( callback ) - a layer attribute (due east.one thousand. proper noun, colour) is changed.

We can apply these to listen for user interactions with the cartoon tools for a responsive and personalized, World Engine experience!

Example: Classification With User-Drawn Geometries

The terminal case is adapting the Classifications example to exist more than interactive. This example runs a machine learning classification using points defined in three FeatureCollections, i for urban, vegetation, and water. Currently, if we wanted to rerun the nomenclature on new points, we would accept to run the script later we edit the points. What if we wanted to rerun the classification automatically when nosotros edit the points?

Nosotros can refactor the nomenclature to rely on the points in the drawing tools, which are ever kept upwards to date. To make this easier, we can use the getEeObject() method on the GeometryLayer to get the Earth Engine object associated with a GeometryLayer (e.g. the underlying FeatureCollection).

Starting time, import the feature collections at the top of the file then nosotros accept urban, vegetation, and water in your imports (hover over the text at the pinnacle of the script and press convert on the selection that says ""urban", "vegetation" and "water" can be converted to import records.").

Classifications tools example.

Effigy iii. Nomenclature tools example with imported characteristic collections.

Next, go the geometry layers associated with each of the feature collections, since any edits to the geometries will be reflected on the layer (add to the top of the file):

          // Starter script: https://code.earthengine.google.com/?scriptPath=Examples:Demos/Classification  var drawingTools = Map.drawingTools(); // But allow drawing points. drawingTools.setDrawModes(['bespeak']);  // Get the layers list. var layers = drawingTools.layers();  // Assuming the gild is urban, vegetation, then water. var urbanLayer = layers.get(0); var vegetationLayer = layers.get(1); var waterLayer = layers.go(2);                  

Now that we have the layers, we can get the FeatureCollection associated with each layer by calling the getEeObject() method.

Change the following line from:

          // Merge the three geometry layers into a unmarried FeatureCollection. var newfc = urban.merge(vegetation).merge(water);                  

to:

          // Merge the three geometry layers into a single FeatureCollection. var urbanfc = urbanLayer.getEeObject(); var vegetationfc = vegetationLayer.getEeObject(); var waterfc = waterLayer.getEeObject(); var newfc = urbanfc.merge(vegetationfc).merge(waterfc);                  

and run the script to see that everything stayed the same. Next, we volition attach event listeners to the cartoon tools to rerun the classification whenever a geometry is changed.

First, we will package all of the classification code into a unmarried function chosen allocate and make some modifications to the Map.addLayer calls to accommodate for the function running multiple times:

          function classify() {   // Merge the three geometry layers into a unmarried FeatureCollection.   var urbanfc = urbanLayer.getEeObject();   var vegetationfc = vegetationLayer.getEeObject();   var waterfc = waterLayer.getEeObject();   var newfc = urbanfc.merge(vegetationfc).merge(waterfc);    // Utilize these bands for classification.   var bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];   // The name of the property on the points storing the class label.   var classProperty = 'landcover';    // Sample the composite to generate grooming data.  Note that the   // class label is stored in the 'landcover' property.   var training = composite.select(bands).sampleRegions({     collection: newfc,     properties: [classProperty],     scale: 30   });    // Railroad train a CART classifier.   var classifier = ee.Classifier.smileCart().railroad train({     features: preparation,     classProperty: classProperty,   });   // Print some info about the classifier (specific to CART).   print('CART, explained', classifier.explain());    // Classify the composite.   var classified = composite.allocate(classifier);    var palette = {min: 0, max: 2, palette: ['red', 'green', 'blue']};   var layer = ui.Map.Layer(classified, palette, 'classified');   Map.layers().gear up(0, layer);    // Optionally, do some accuracy cess.  Fist, add a column of   // random uniforms to the training dataset.   var withRandom = grooming.randomColumn('random');    // We want to reserve some of the data for testing,   // to avoid overfitting the model.   var split = 0.vii; // Roughly lxx% preparation, 30% testing.   var trainingPartition = withRandom.filter(ee.Filter.lt('random', split));   var testingPartition = withRandom.filter(ee.Filter.gte('random', divide));    // Trained with 70% of our data.   var trainedClassifier = ee.Classifier.smileRandomForest(5).train({     features: trainingPartition,     classProperty: classProperty,     inputProperties: bands   });    // Classify the exam FeatureCollection.   var test = testingPartition.classify(trainedClassifier);    // Impress the defoliation matrix.   var confusionMatrix = test.errorMatrix(classProperty, 'nomenclature');   print('Confusion Matrix', confusionMatrix); }  classify();                  

Now, when we call classify, the script will use the up-to-date version of the imports to run the classifier, which means we don't need to rerun the script to capture new points!

We add event listeners to detect when the user has made any edits to the geometries and run the classify function. We as well use ui.util.debounce to merely run it once every 100 milliseconds to prevent it from firing likewise fast, like when the user drags a point effectually.

          // Use debounce to call the office at most every 100 milliseconds. drawingTools.onEdit(ui.util.debounce(classify, 100)); drawingTools.onDraw(ui.util.debounce(classify, 100)); drawingTools.onErase(ui.util.debounce(allocate, 100));  Map.centerObject(urban);                  

That'south all there is to it! Hither'southward a link to the full script.

Determination

The drawing tools API provides a rich fix of features that give yous full control of the client-side geometries in your Earth Engine scripts and apps. We're excited to come across what you build with them!

Bonus

Since you lot made it this far down, here'southward an Earth Engine script that recreates the drawing tools from scratch :)

welchagained.blogspot.com

Source: https://developers.google.com/earth-engine/tutorials/community/drawing-tools

0 Response to "Draw Circle on Google Earth Pro"

Post a Comment

Iklan Atas Artikel

Iklan Tengah Artikel 1

Iklan Tengah Artikel 2

Iklan Bawah Artikel