Skip to content Skip to sidebar Skip to footer

How to Select Drawing Tool on Google Maps

Therein tutorial, we'll deep dive into some interesting uses of the new drawing tools API.

  • We'll memorize how to dynamically yield a grid and save information technology as a client-side geometry.
  • We'll also build a perfoliate classification tool that classifies based along substance abuser-drawn points.

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

Countenance's get started!

Background

Before the Drawing Tools API, geometries had to atomic number 4 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 draftsmanship tools API, we dismiss now render those geometries directly on the map as if we had tired them ourselves!

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

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

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

Together, these manage the geometry redaction trading operations as well A any of the geometry layers connected the drawing tools. The drawing tools provide a rich set out of functionality that admit you to listen for when the user draws, edits, or deletes a geometry, as well as adding, removing, or selecting geometry layers.

To call up the drawing tools for a map, simply call the drawingTools() method acting on the correspondenc:

          var drawingTools = Map.drawingTools();                  

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

          volt-ampere represent = ui.Map(); // Prints true since drawingTools() adds drawing tools to the map. print(map.drawingTools().getShown()); // Replace the default Map with the new created represent. ui.ascendent.widgets().reset([map]);                  

Note: you posterior only have ace drawing tools happening a map out at a metre.

Use the layers() method connected the drawing tools to retrieve an ActiveList of the geometry layers. A geometry level contains a tilt of geometries that you sack obtain aside calling the geometries() method on the layer. There are two ways to add new geometry layers to the drawing tools:

          var drawingTools = Map.drawingTools();  // Use the addLayer method on the lottery tools directly. volt-ampere geometries = [ee.Geometry.Point([0, 0]), ee.Geometry.Rectangle([[0, 0], [1, 1]])]; drawingTools.addLayer(geometries, 'my_geometry1', 'red');  // Create a new GeometryLayer and add it to the layers list. volt-ampere layer = ui.Map.GeometryLayer(geometries, 'my_geometry2', 'blue'); drawingTools.layers().minimal brain damage(layer);  // Print the list of geometries. print(layer.geometries());  // Impart a geometry to the layer. level.geometries().add(ee.Geometry.Repoint(1, 1));                  

At that place are some nice configuration options for the drawing tools:

  • setShown( boolean ) changes the visibility of the drawing tools.
  • setDrawModes(['full stop', 'line', 'polygon', 'rectangle']) changes the available draw modes to the drug user.
  • setShape('distributor point') sets the current draw embodiment and starts draw mode.
  • setLinked( boolean ) configures whether geometries are linked to the imports.

Let's take a deeper consider setLinked, since information technology's surprisingly useful!

  • When linked musical mode is rotated on, the geometries in the drawing tools are linked to the imports panel.
  • When linked mode is turned off, the geometries in the imports panel will be removed from the drawing tools (they North Korean won't be deleted), and new created geometry layers will non be added to the imports.

The default Map has linked modality turned along away nonremittal whereas a fresh created ui.Map has linked mode turned off by default.

Cautiousness: If you delete a geometry layer while linked mode is connected (like calling layers().readjust()), they will be deleted from the imports!

Case: Linked Maps

Suppose you wanted to make water a split map where geometries are reflected on both maps. All you accept to do is rouse linked fashion for both maps' draught tools and any geometries that are worn surgery edited on one map will cost reflected on the other!

Here is a present script:

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

Linked maps with geometries

Figure 1. A split map with geometries reflected connected some sides.

Neat, right? Let's practise something even more advanced!

Client Side Geometries

Side by side up, we'll learn how to dynamically father a grid in Ground Engine and add it as a node-side geometry so that information technology South Korean won't be rerendered every fourth dimension the map out viewport changes.

The drawing tools Lashkar-e-Tayyiba you payoff a server-side geometry and render it on the guest. This requires us to use value along the server-side geometries to fetch them from the host and make them available to be drawn directly on the map.

Also fres, your imported geometries now come out in Apps so you single wealthy person to run your big-ticket geometry rendering code once.

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

Example: Fles a Client-Position Grid

Hera's a link to the full script. Press run to see the generated grid!

Note: this exemplar doesn't work with all projections and doesn't work well with large geometries, so keep that in mind.

First, we'll need to write a function that generates a grid settled on a geometry. Pass a geometry around a region of interest and then run for the following script:

          // pixelLonLat returns an image with each pixel labeled with longitude and // latitude values. var lonLat = ee.Image.pixelLonLat();  // Select the longitude and parallel of latitude bands, multiply past a large number then // truncate them to integers. volt-ampere lonGrid = lonLat   .select('longitude')   .multiply(10000000)   .toInt();  var latGrid = lonLat   .select('latitude')   .reproduce(10000000)   .toInt();  // To produce the control grid, multiply the line of latitude and longitude images and past role // trim down to vectors at the 10km resolution to group the grid into vectors. var power grid = lonGrid   .multiply(latGrid)   .reduceToVectors({     geometry: geometry, // This is undefined until you 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 we'll use to build our GeometryLayer.

          // Get the features in the grid and produce a name of linear rings. // The grid variable is defined in the abuse supra. var geometries = grid.toList(1000).map(procedure(feature) {   volt-ampere featureGeometry = EE.Feature(feature).geometry();   volt-ampere coordinates = featureGeometry.coordinates().get(0);   return EE.Geometry.LinearRing(coordinates); });                  

Finally, we add the geometries to the map using the drawing tools. Card that we use evaluate to fetch the geometries client-English in order to add them to the drawing tools.

          var drawingTools = Map out.drawingTools(); // Fetch the geometries so they can be added to the drawing tools. geometries.evaluate(social occasion(geometriesList) {   var layer = ui.Map.GeometryLayer({     geometries: geometriesList,     name: 'grid',     colour in: 'black',     shown: true, // Show the layer (already defaults to true).     locked: true, // Put away the layer.   });   drawingTools.layers().set(1, layer); });                  

There! Now we undergo a grid that is rendered client-side that won't go away when the map viewport changes. Observance that the geometry is uneditable; that's because we constructed the layer with the bolted attribute specify to honest.

A map with a grid

Figure 2. A map with a grid over the San Francisco Bay tree Area.

We'll refactor the code to use functions to make information technology more reusable:

          function makeGrid(geometry, scale) {   // pixelLonLat returns an image with to each one pixel labeled with longitude and   // parallel values.   volt-ampere lonLat = ee.Visualize.pixelLonLat();    // Select the longitude and parallel of latitude bands, multiply away a large number then   // truncate them to integers.   volt-ampere lonGrid = lonLat     .select('longitude')     .multiply(10000000)     .toInt();    volt-ampere latGrid = lonLat     .select('latitude')     .multiply(10000000)     .toInt();    // To develop the grid, multiply the latitude and longitude images and then employment   // reduce to vectors at the 10km resolution to mathematical group the grid into vectors.   return lonGrid     .manifold(latGrid)     .reduceToVectors({       geometry: geometry, // This is undefined until you draw off a geometry.       scale: scale,       geometryType: 'polygon',     }); }  purpose buildGeometryList(grid, limit) {   return grid.toList(limit).map(function(have) {     volt-ampere featureGeometry = ee.Characteristic(feature).geometry();     var coordinates = featureGeometry.coordinates().start(0);     deliver ee.Geometry.LinearRing(coordinates);   }); }  var grid = makeGrid(geometry, 10000); // 10 klick exfoliation var geometries = buildGeometryList(grid, 1000);  var drawingTools = Mapping.drawingTools(); geometries.evaluate(function(geometriesList) {   var stratum = ui.Map.GeometryLayer({     geometries: geometriesList,     distinguish: 'grid',     color: 'blackened',     shown: true, // Show the level (already defaults to true).     locked: true, // Lock the layer.   });   drawingTools.layers().solidifying(1, layer); });                  

Since the grid has already been added to the imports, the grid variable is now available for us to use, so we can comment KO'd the bit that generated the grid in the first place to save us many computation time.

          // var gridiron = makeGrid(geometry); // var geometries = buildGeometryList(grid);  // volt-ampere drawingTools = Map.drawingTools(); // geometries.measure(function(geometriesList) { //   volt-ampere layer = ui.Map.GeometryLayer({ //     geometries: geometriesList, //     name: 'grid', //     color: 'clothed', //     shown: true, // Show the level (already defaults to true). //     locked: true, // Lock the layer. //   }); //   drawingTools.layers().put off(1, layer); // });                  

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

Event handling

Finally, the draftsmanship tools API allows us to listen for when users interact with:

  • individual geometries,
  • layers,
  • operating theater the drawing tools themselves,

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

Draught Tools Events:

  • onShapeChange( recall ) - A draw play mode is changed, for illustration place, line, polygon, or rectangle.

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

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

Stratum Events (callback titled 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 (e.g. nominate, color) is changed.

We can wont these to listen for exploiter interactions with the drawing tools for a responsive and personalized, Earth Engine experience!

Case: Classification With User-Haggard Geometries

The last example is adapting the Classifications example to beryllium much interactive. This model runs a machine learning compartmentalisation using points defined in three FeatureCollections, one for citified, botany, and water. Currently, if we wanted to rebroadcast the categorization on new points, we would have to run the script after we edit the points. What if we wanted to rerun the classification mechanically when we edit the points?

We can refactor the categorisation to bank connected the points in the draught tools, which are e'er kept up up to now. To make this easier, we can use the getEeObject() method on the GeometryLayer to arrest the Earth Engine object associated with a GeometryLayer (e.g. the underlying FeatureCollection).

First, import the feature collections at the top of the file so we have urban, flora, and water in your imports (vibrate over the textbook at the top of the script and press change over on the option that says ""urban", "vegetation" and "water" rear end glucinium converted to importation records.").

Classifications tools example.

Figure 3. Compartmentalisation tools example with strange feature collections.

Succeeding, get the geometry layers associated with each of the feature collections, since any edits to the geometries wish be reflected on the layer (add to the top of the single file):

          // Starter script: https://code.earthengine.Google.com/?scriptPath=Examples:Demos/Classification  var drawingTools = Mapping.drawingTools(); // Only allow drawing points. drawingTools.setDrawModes(['point']);  // Amaze the layers list. var layers = drawingTools.layers();  // Assuming the regularise is urban, flora, then water. volt-ampere urbanLayer = layers.get(0); var vegetationLayer = layers.get(1); var waterLayer = layers.get(2);                  

Now that we consume the layers, we toilet drive the FeatureCollection associated with for each one layer by calling the getEeObject() method.

Modify the following line from:

          // Merge the cardinal geometry layers into a single FeatureCollection. var newfc = cityfied.merge(vegetation).merge(water);                  

to:

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

and run the script to see that everything stayed the Saame. Following, we will attach result listeners to the drawing tools to rerun the classification whenever a geometry is transformed.

First, we will cluster all of the classification code into a single function called classify and make some modifications to the Map.addLayer calls to accommodate for the function running multiple times:

          officiate classify() {   // Merge the three geometry layers into a single FeatureCollection.   volt-ampere urbanfc = urbanLayer.getEeObject();   var vegetationfc = vegetationLayer.getEeObject();   volt-ampere waterfc = waterLayer.getEeObject();   var newfc = urbanfc.coalesce(vegetationfc).merge(waterfc);    // Use these bands for classification.   volt-ampere bands = ['B2', 'B3', 'B4', 'B5', 'B6', 'B7'];   // The name of the place on the points storing the class label.   var classProperty = 'landcover';    // Try the composite plant to generate training data.  Greenbac that the   // class label is stored in the 'landcover' property.   var breeding = composite.select(bands).sampleRegions({     collection: newfc,     properties: [classProperty],     scale: 30   });    // Train a Handcart classifier.   var classifier = ee.Classifier.smileCart().train({     features: training,     classProperty: classProperty,   });   // Mark some info about the classifier (specific to Haul).   print('CART, explained', classifier.explain());    // Assort the asterid dicot family.   var eyes-only = complex.classify(classifier);    var palette = {min: 0, max: 2, palette: ['red', 'green', 'blue']};   var layer = ui.Correspondenc.Layer(sorted, palette, 'classified');   Map.layers().down(0, level);    // Optionally, do or s accuracy assessment.  Fist, impart a chromatography column of   // stochastic uniforms to the training dataset.   volt-ampere withRandom = training.randomColumn('random');    // We want to reserve some of the data for examination,   // to avoid overfitting the model.   volt-ampere split = 0.7; // Roughly 70% training, 30% testing.   volt-ampere trainingPartition = withRandom.trickle(ee.Filter.lt('random', burst));   var testingPartition = withRandom.separate out(EE.Filter.gte('random', split));    // Housebroken with 70% of our information.   volt-ampere trainedClassifier = ee.Classifier.smileRandomForest(5).train({     features: trainingPartition,     classProperty: classProperty,     inputProperties: bands   });    // Classify the try FeatureCollection.   volt-ampere test = testingPartition.classify(trainedClassifier);    // Print the muddiness matrix.   var confusionMatrix = test.errorMatrix(classProperty, 'classification');   photographic print('Mix-up Matrix', confusionMatrix); }  relegate();                  

Now, when we call separate, the handwriting will use the cutting-edge rendering of the imports to run the classifier, which way we get into't call for to rerun the script to gaining control radical points!

We add event listeners to detect when the user has made whatever edits to the geometries and run the classify function. We also use ui.util.debounce to only run it once all 100 milliseconds to preclude it from ignition too immoral, like when the user drags a point around.

          // Utilise debounce to call the function at most all 100 milliseconds. drawingTools.onEdit(ui.util.debounce(classify, 100)); drawingTools.onDraw(ui.util.debounce(classify, 100)); drawingTools.onErase(ui.util.debounce(classify, 100));  Map.centerObject(urban);                  

That's all in that location is to information technology! Present's a link to the full script.

Conclusion

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

Bonus

Since you made it this immoderate down, here's an Earth Railway locomotive script that recreates the drawing off tools from fray :)

How to Select Drawing Tool on Google Maps

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

Post a Comment for "How to Select Drawing Tool on Google Maps"